diff --git a/.agents/skills/api-and-namespace-design/SKILL.md b/.agents/skills/api-and-namespace-design/SKILL.md new file mode 100644 index 00000000..85b51222 --- /dev/null +++ b/.agents/skills/api-and-namespace-design/SKILL.md @@ -0,0 +1,140 @@ +--- +name: api-and-namespace-design +description: API design conventions, namespace coordinate system, RBAC roles, ClawHub compatibility layer, OpenAPI contract sync rules, and CSRF/session handling. +license: Apache-2.0 +--- + +# API and Namespace Design Skill + +## Trigger + +Use this skill when: +- Adding or modifying REST API endpoints +- Changing namespace, skill, or user coordinate logic +- Working on ClawHub CLI compatibility layer +- Modifying OpenAPI specifications or generated types +- Adding new admin or governance endpoints + +## Namespace Coordinate System + +SkillHub uses a two-axis coordinate model: + +``` +@{namespace_slug}/{skill_slug} +``` + +- `@global/my-skill` — Global namespace skill +- `@my-team/my-skill` — Team namespace skill (namespace slug is any valid slug) +- `@department-ops/my-skill` — Department namespace skill + +### Namespace Model + +Namespaces (`domain/namespace/`): +- **Slug**: unique identifier, validated by `SlugValidator` +- **Status**: `ACTIVE`, `FROZEN`, `ARCHIVED` +- **Roles**: `OWNER`, `ADMIN`, `MEMBER` +- Frozen or archived namespaces cannot publish skills + +### RBAC Roles + +**Namespace-level** (`domain/namespace/NamespaceRole`): +- `OWNER` — Full control over namespace and all skills +- `ADMIN` — Can manage members, archive skills, publish +- `MEMBER` — Can publish skills to the namespace + +**Platform-level**: +- `SUPER_ADMIN` — Bypasses all permission checks, can publish directly without review + +## ClawHub Compatibility Layer + +ClawHub CLI uses a single-slug model (no `/` allowed in slugs). Mapping: + +| SkillHub Coordinate | Canonical Slug | Notes | +|---------------------|----------------|-------| +| `@global/my-skill` | `my-skill` | Global namespace omits prefix | +| `@team-name/my-skill` | `team-name--my-skill` | Double-dash separator | + +**Conflict resolution**: `--` split takes priority. `@global/team-name--my-skill` would conflict +with `@team-name/my-skill`, resolved to the team namespace skill. Global skill slugs must NOT +contain `--`. + +## API Design + +### Controllers + +- Controllers in `skillhub-app` (`com.iflytek.skillhub.controller/`) are **transport only** +- Responsibilities: extract auth context, bind request params, wrap responses +- Complex business logic belongs in domain services (`skillhub-domain`) or app services +- Use Springdoc OpenAPI annotations (`@Operation`, `@ApiResponse`) for API documentation +- User identity is always **String** in API inputs and outputs + +### Request/Response Patterns + +- DTOs in `com.iflytek.skillhub.dto/` +- `ReviewTaskRequest` / `ReviewTaskResponse` for review workflow +- Response wrapping handled at controller layer +- Validation errors use `DomainBadRequestException` with i18n message keys + +### Session and CSRF + +- Session-based auth with cookie storage +- CSRF protection via `XSRF-TOKEN` cookie and `X-XSRF-TOKEN` header +- Smoke tests validate the full register → login → CSRF → action → logout flow +- Mock auth uses `X-Mock-User-Id` header in local dev + +### Well-known Discovery + +`/.well-known/clawhub.json` returns `{ "apiBase": "/api/v1" }` for ClawHub CLI auto-discovery. + +## OpenAPI Contract Sync + +When backend API contracts change: + +```bash +make generate-api +``` + +This runs `openapi-typescript http://localhost:8080/v3/api-docs -o src/api/generated/schema.d.ts`. + +Commit the updated `web/src/api/generated/schema.d.ts` with the PR. + +To verify no drift: + +```bash +./scripts/check-openapi-generated.sh +``` + +This starts local dependencies, boots the backend, regenerates the schema, and fails if the +checked-in SDK is stale. + +## Versioning and Tags + +- Semantic versioning for skill versions (`major.minor.patch`) +- `latest` tag is system-reserved, read-only, auto-follows `Skill.latestVersionId` +- Custom tags (`stable`, `beta`) are manually maintained +- `latest` cannot be moved manually +- Auto-generated versions use `yyyyMMdd.HHmmss` format when no version is specified in SKILL.md + +## Key API Endpoints + +| Method | Path | Purpose | +|--------|------|---------| +| `GET` | `/api/v1/auth/me` | Current user info (401 if unauthenticated) | +| `POST` | `/api/v1/auth/local/login` | Local account login | +| `POST` | `/api/v1/auth/local/register` | Local account registration | +| `POST` | `/api/v1/auth/logout` | Logout (302/200/204) | +| `POST` | `/api/v1/auth/local/change-password` | Password change | +| `GET` | `/api/v1/namespaces` | List namespaces | +| `GET` | `/api/v1/labels` | List visible labels (public) | +| `POST` | `/api/v1/admin/labels` | Create label definition (admin) | +| `DELETE` | `/api/v1/admin/labels/{slug}` | Delete label definition (admin) | +| `GET` | `/actuator/health` | Health check | +| `GET` | `/actuator/prometheus` | Prometheus metrics | + +## Common Pitfalls + +- Forgetting CSRF token on POST/PUT/DELETE requests (needs `X-XSRF-TOKEN` header) +- Using numeric user IDs in API — all user identities are **String** +- Not regenerating OpenAPI types after adding/changing endpoints +- Putting business logic in controllers instead of domain/app services +- Assuming namespace slugs follow a specific prefix pattern — they are arbitrary valid slugs diff --git a/.agents/skills/backend-module-structure/SKILL.md b/.agents/skills/backend-module-structure/SKILL.md new file mode 100644 index 00000000..ef375e1b --- /dev/null +++ b/.agents/skills/backend-module-structure/SKILL.md @@ -0,0 +1,108 @@ +--- +name: backend-module-structure +description: Rules for the SkillHub backend Maven multi-module clean architecture. Ensures agents place new code in the correct module and respect dependency direction. +license: Apache-2.0 +--- + +# Backend Module Structure Skill + +## Trigger + +Use this skill when: +- Adding or modifying Java backend code +- Creating new services, controllers, repositories, or entities +- Refactoring backend code across files +- Reviewing backend code placement + +## Rules + +### Dependency Direction + +The design-doc dependency direction: + +``` +app → domain, auth, search, storage, infra, notification +infra → domain # implements domain repository interfaces +auth → domain +search → domain +notification → domain +storage → (independent) # pure SPI +``` + +**Design intent**: `skillhub-domain` should be the innermost layer, defining entities, +repository interfaces, and domain services without depending on infra, auth, search, or storage. + +**Code reality**: `skillhub-domain` declares a Maven dependency on `skillhub-storage` (via +`pom.xml`), and several domain services (`SkillHardDeleteService`, `SkillDownloadService`, +`SkillPublishService`, `SkillGovernanceService`, `SkillQueryService`, +`SkillStorageDeletionCompensationService`) import `com.iflytek.skillhub.storage.ObjectStorageService`. +This is an existing deviation from the ideal clean architecture. New code should avoid adding +further cross-module dependencies from domain. + +### Where to Place Code + +| Code Type | Module | Java Package | +|-----------|--------|-------------| +| Entity / Value Object | skillhub-domain | `com.iflytek.skillhub.domain.{submodule}/` | +| Repository Interface | skillhub-domain | `com.iflytek.skillhub.domain.{submodule}/` | +| Domain Service | skillhub-domain | `com.iflytek.skillhub.domain.{submodule}/service/` | +| Domain Event | skillhub-domain | `com.iflytek.skillhub.domain/event/` | +| Domain Exception | skillhub-domain | `com.iflytek.skillhub.domain/shared/exception/` | +| JPA Repository Impl | skillhub-infra | `com.iflytek.skillhub.infra.repository/` | +| Controller | skillhub-app | `com.iflytek.skillhub.controller/` | +| App Service | skillhub-app | `com.iflytek.skillhub.service/` | +| Query Repository | skillhub-app | `com.iflytek.skillhub.repository/` | +| DTO / Response | skillhub-app | `com.iflytek.skillhub.dto/` | +| OAuth2 / Auth Config | skillhub-auth | `com.iflytek.skillhub.auth/` | +| Search SPI / Impl | skillhub-search | `com.iflytek.skillhub.search/` | +| Storage SPI / Impl | skillhub-storage | `com.iflytek.skillhub.storage/` | +| Notification Service | skillhub-notification | `com.iflytek.skillhub.notification/` | + +### Maven Modules + +The parent POM (`server/pom.xml`) defines 7 modules with `spring-boot-starter-parent:3.2.3`: + +``` +skillhub-app | skillhub-domain | skillhub-auth | skillhub-search +skillhub-storage | skillhub-infra | skillhub-notification +``` + +### Repository vs Query Repository + +- **Domain Repository** (`skillhub-domain`): Aggregate reads, state transitions, rule evaluation. + Returns domain objects. Defined as interfaces, implemented in `skillhub-infra` via Spring Data JPA. +- **Query Repository** (`com.iflytek.skillhub.repository`): Read-model assembly, joins multiple + sources, presentation projection. Returns DTOs. Implemented directly in `skillhub-app`. + +Current query repositories: +- `GovernanceQueryRepository` / `JpaGovernanceQueryRepository` +- `MySkillQueryRepository` / `JpaMySkillQueryRepository` +- `ProfileReviewQueryRepository` / `JpaProfileReviewQueryRepository` +- `AdminSkillReportQueryRepository` / `JpaAdminSkillReportQueryRepository` + +When a new read use case arrives: +1. If it's for state transition or domain rule → domain repository port +2. If it's for page/list/detail response assembly with joins → app query repository +3. If it's a thin single-aggregate read → direct domain repository call from app service +4. If direct SQL/EntityManager is needed → add class-level comment explaining why + +### Building Backend Tests + +Never run `./mvnw -pl skillhub-app clean test` directly under `server/`. Use: +```bash +make test-backend-app # skillhub-app + dependencies (includes -am) +make test-backend # all backend modules +``` + +Running clean test on skillhub-app alone can fall back to stale artifacts from the local Maven +repository, surfacing misleading `cannot find symbol` and signature-mismatch errors. + +### User Identity Type + +User identity is **always String** throughout the codebase. This covers: +- Authentication, API params, permissions, audit +- Resource owner, creator, reviewer, actor, submittedBy +- All user-associated fields + +The `UserAccount` entity uses `@Column(length = 128)` for its ID. The platform needs to support +external SSO/OIDC/SCIM identity sources whose UIDs are typically stable strings. diff --git a/.agents/skills/code-conventions/SKILL.md b/.agents/skills/code-conventions/SKILL.md new file mode 100644 index 00000000..2add2e31 --- /dev/null +++ b/.agents/skills/code-conventions/SKILL.md @@ -0,0 +1,135 @@ +--- +name: code-conventions +description: Code style, logging, and testing conventions for SkillHub backend (Java) and frontend (TypeScript). Use when writing or reviewing code. +license: Apache-2.0 +--- + +# Code Conventions Skill + +## Java / Backend Conventions + +### User Identity Type + +User identity is **always `String`** throughout the codebase. This covers: +- Authentication and authorization +- API parameters and responses +- Permission checks +- Audit logs +- Resource owner, creator, reviewer, actor, submittedBy fields + +Never introduce `int`, `long`, or `bigint` as user identifiers. The platform needs to support +external SSO/OIDC/SCIM identity sources whose UIDs are typically stable strings. + +### Exception Handling + +- Use `LocalizedDomainException` for user-facing error messages (supports i18n) +- Use `DomainBadRequestException` for invalid client input +- Use `DomainNotFoundException` for missing resources +- Use `DomainForbiddenException` for authorization failures +- Exception classes live in `skillhub-domain/shared/exception/` + +### Domain Services + +- Return domain objects, not DTOs +- Contain business rules and state transitions +- Use domain events for cross-cutting side effects (publishing, notifications) +- Located in `domain/{submodule}/service/` + +### Controllers + +- Transport only: extract auth context, bind request params, wrap responses +- No business logic in controllers +- Located in `com.iflytek.skillhub.controller/` + +### Query Repositories + +- Handle read-model joins and presentation projection +- Return DTOs or presentation models +- Located in `com.iflytek.skillhub.repository/` +- Named like `*QueryRepository` (e.g., `GovernanceQueryRepository`, `MySkillQueryRepository`) + +### App Services + +- Workflow orchestration: coordinate domain services and query repositories +- Should express "what this endpoint does", not "how it assembles DTOs" +- Located in `com.iflytek.skillhub.service/` + +### Logging + +- Use SLF4J with structured logging +- Use MDC for request tracing +- Log at appropriate levels: INFO for business events, DEBUG for troubleshooting, ERROR for failures + +## TypeScript / Frontend Conventions + +### Type Safety + +- Strict TypeScript mode. No `any` types. +- Use generated OpenAPI types from `web/src/api/generated/schema.d.ts` for all API interactions. +- Additional types in `web/src/types/` + +### Data Fetching + +- **Always use TanStack Query** (`@tanstack/react-query`) for server state +- **Never use `useEffect`** for data fetching +- Use `openapi-fetch` client for type-safe API calls + +### Component Composition + +- **Radix UI** primitives: `@radix-ui/react-dropdown-menu`, `@radix-ui/react-select` +- **class-variance-authority** (cva) for component variants +- **clsx** + **tailwind-merge** for class merging +- **`cn()` utility**: `web/src/shared/lib/utils.ts` +- shadcn/ui is NOT used as a library + +### State Management + +- **TanStack Query** for server state (API data, caching, invalidation) +- **Zustand** for local/UI state (theme, sidebar, modals, form state) + +### Feature-Sliced Design + +| Layer | Path | Purpose | +|-------|------|---------| +| Pages | `web/src/pages/` | Route-level page components | +| Features | `web/src/features/` | Self-contained business features | +| Entities | `web/src/entities/` | Domain entity display logic | +| Shared | `web/src/shared/` | Reusable UI components, hooks, utilities | + +Place code at the lowest appropriate layer. Do not put page-level logic in shared. + +### Styling + +- Tailwind CSS for all styling +- Follow existing component patterns +- Use `cn()` for conditional class merging + +### Internationalization + +- Use i18next + react-i18next +- All user-facing text must be translatable +- Translation keys in `web/src/i18n/` + +## Testing Philosophy + +### Backend + +- JUnit 5 + Mockito + AssertJ +- Use Spring Boot test slices where possible (`@WebMvcTest`, `@DataJpaTest`) +- Test behaviors, not implementations +- Use `make test-backend-app` (includes `-am` for dependent modules) +- Never run `./mvnw -pl skillhub-app clean test` directly — stale Maven cache causes misleading errors + +### Frontend + +- Vitest for unit tests +- Playwright for E2E tests +- Test component behavior and user interactions + +## Common Pitfalls + +- **Maven multi-module**: Always use `-am` flag or Makefile targets to include dependent modules +- **OpenAPI types**: Must regenerate and commit after API contract changes +- **String identity**: Never use numeric types for user identifiers +- **Controller business logic**: Move to domain service or app service +- **Complex read-models in app service**: Extract to query repository diff --git a/.agents/skills/dev-workflow/SKILL.md b/.agents/skills/dev-workflow/SKILL.md new file mode 100644 index 00000000..8f71ee75 --- /dev/null +++ b/.agents/skills/dev-workflow/SKILL.md @@ -0,0 +1,194 @@ +--- +name: dev-workflow +description: The complete development workflow for SkillHub contributors including local dev, staging validation, testing, and PR creation. Ensures agents follow the correct sequence of steps. +license: Apache-2.0 +--- + +# Development Workflow Skill + +## Trigger + +Use this skill when: +- Starting local development +- Running tests or validation +- Preparing a pull request +- Setting up the development environment +- Working with parallel agent worktrees + +## Prerequisites + +- Java 21+ (`java -version`) +- Maven wrapper (`./mvnw` in `server/`) +- Node.js + pnpm +- Docker + docker compose +- `gh` CLI (for PR creation) +- `curl` (for smoke tests and health checks) + +## Workflow Stages + +### Stage 1: Local Development (fast iteration) + +**One-command start:** + +```bash +make dev-all # Start full stack: Postgres, Redis, MinIO, scanner, backend, frontend +make dev-all-down # Stop everything +make dev-all-reset # Full reset (clears data volumes) +make dev-status # Check service status +``` + +**Access points:** +- Web UI: `http://localhost:3000` +- Backend API: `http://localhost:8080` +- Scanner: `http://localhost:8000` + +**Individual components:** + +```bash +make dev # Start dependency services only (Postgres, Redis, MinIO, scanner) +make dev-server # Start backend in foreground (blocking) +make dev-web # Start Vite dev server (HMR enabled) +make dev-server-restart # Restart backend process +make dev-down # Stop dependency services +make dev-logs # View backend logs (use SERVICE=frontend for frontend logs) +``` + +**Backend development**: After editing Java code, run `make dev-server-restart`. + +**Frontend development**: Vite HMR enabled — save a file for instant browser updates. + +**Scanner**: The security scanner is enabled by default in dev. Health checked at `http://localhost:8000/health`. + +### Stage 2: Testing + +| Command | Scope | Notes | +|---------|-------|-------| +| `make test-backend-app` | Backend unit tests | skillhub-app + dependencies (`-am`) | +| `make test-backend` | All backend modules | All modules via `./mvnw test` | +| `make test-frontend` | Frontend unit tests | Vitest (pnpm run test) | +| `make test-e2e-frontend` | Frontend E2E tests | Playwright | +| `make test-e2e-smoke-frontend` | Frontend E2E smoke | Playwright subset | +| `make typecheck-web` | TypeScript type check | `tsc --noEmit` | +| `make lint-web` | ESLint check | Frontend linting | + +**Important**: Never run `./mvnw -pl skillhub-app clean test` directly under `server/`. +Use `-am` or Makefile targets to include dependent modules. + +### Stage 3: Staging Regression (pre-PR validation) + +```bash +make staging # Build backend Docker image + frontend static + smoke test +make staging-down # Tear down +make staging-logs # View backend logs +SERVICE=web make staging-logs # View Nginx logs +``` + +Staging validates the containerized deployment path: +- Backend: built as Docker image from local source (`Dockerfile.dev`) +- Frontend: built as static files (`pnpm build`), served by Nginx +- Dependencies: same Postgres/Redis/MinIO as local dev +- Smoke test runs against staging via `scripts/smoke-test.sh` + +**Staging URLs:** +- Web UI: `http://localhost` +- Backend API: `http://localhost:8080` + +**Staging credentials** (for bootstrap admin): +- Username: `admin` +- Password: `Admin@staging2026` + +### Stage 4: Pull Request + +```bash +make pr # Push branch + create PR (requires gh CLI) +``` + +Requirements: +- `gh` CLI installed and authenticated +- Not on main/master branch +- All changes committed (will prompt if uncommitted changes exist) + +### Useful Commands + +| Command | Description | +|---------|-------------| +| `make generate-api` | Regenerate OpenAPI types from running backend | +| `make namespace-smoke` | Namespace workflow smoke test | +| `make db-reset` | Reset database only (Flyway migrate) | +| `make validate-release-config` | Validate release env vars (.env.release) | +| `./scripts/smoke-test.sh` | Basic API smoke test (health, auth, labels) | +| `./scripts/namespace-smoke-test.sh` | Namespace CRUD + membership smoke test | +| `./scripts/check-openapi-generated.sh` | Verify OpenAPI types are not stale | +| `make parallel-init TASK=name` | Create parallel worktree for agent | + +### Mock Auth Users + +| User ID | Role | Header | +|---------|------|--------| +| `local-user` | Regular user | `X-Mock-User-Id: local-user` | +| `local-admin` | Super admin | `X-Mock-User-Id: local-admin` | + +Bootstrap admin (local profile): +- Username: `admin` +- Password: `ChangeMe!2026` + +### Smoke Test Coverage + +`scripts/smoke-test.sh` validates: +1. Health endpoint (`/actuator/health` → 200) +2. Prometheus metrics (`/actuator/prometheus` → 200) +3. Namespaces API (`/api/v1/namespaces` → 200) +4. Auth required (`/api/v1/auth/me` → 401 without session) +5. User registration flow (with CSRF) +6. Auth me with session +7. Password change +8. Logout + verify 401 after +9. Admin login +10. Label CRUD (admin only) + +Additional smoke tests: +- `scripts/namespace-smoke-test.sh` — Namespace creation, membership, publishing +- `scripts/governance-smoke-test.sh` — Governance and moderation +- `scripts/promotion-smoke-test.sh` — Skill promotion between scopes + +### Parallel Agent Workflow + +For parallel agent development with isolated worktrees: + +```bash +make parallel-init TASK=feature-name # Create worktree +make parallel-sync SOURCES="feat1 feat2" # Merge feature branches +make parallel-up SOURCES="feat1 feat2" # Merge + start dev environment +make parallel-down # Stop parallel environment +``` + +See `docs/13-parallel-workflow.md` for full details. + +### Commit Style + +Use conventional commit format: + +``` +(): +``` + +Examples: +``` +feat(auth): add local account login +fix(publish): resolve null pointer when skill metadata is missing name +docs(deploy): clarify runtime image usage +test(namespace): add membership service edge case tests +refactor(review): extract query repository for governance list +chore(ci): add parallel workflow scripts +``` + +### Common Issues + +| Issue | Solution | +|-------|----------| +| Backend won't start | Check Java version (`java -version`), must be 21+ | +| Port 8080 in use | `lsof -i :8080` to find and kill the process | +| Maven download timeout | Configure mirror in `~/.m2/settings.xml` | +| Frontend won't start | Run `make web-deps` to ensure node_modules exist | +| Staging build fails | Check `Dockerfile.dev` and ensure Maven build succeeds first | +| CSRF errors in tests | Ensure cookie jar is shared and CSRF token refreshed after login | diff --git a/.agents/skills/frontend-conventions/SKILL.md b/.agents/skills/frontend-conventions/SKILL.md new file mode 100644 index 00000000..be7e647e --- /dev/null +++ b/.agents/skills/frontend-conventions/SKILL.md @@ -0,0 +1,124 @@ +--- +name: frontend-conventions +description: Coding conventions, architecture patterns, and testing rules for the SkillHub React frontend. Ensures agents follow Feature-Sliced Design and use the generated OpenAPI types. +license: Apache-2.0 +--- + +# Frontend Conventions Skill + +## Trigger + +Use this skill when: +- Adding or modifying React/TypeScript frontend code +- Creating new pages, features, entities, or shared components +- Changing API client calls or data fetching patterns + +## Rules + +### Feature-Sliced Design + +Place code at the lowest appropriate layer: + +| Layer | Path | Purpose | +|-------|------|---------| +| Pages | `web/src/pages/` | Route-level page components | +| Features | `web/src/features/` | Business features (search, upload, review, etc.) | +| Entities | `web/src/entities/` | Domain entity display logic (skill, user, namespace) | +| Shared | `web/src/shared/` | Reusable UI components, hooks, utilities | + +Current features: +- `admin` — Admin panel (user management, labels, search) +- `auth` — Login, OAuth flows, device auth +- `governance` — Skill governance actions (hide, yank, archive) +- `namespace` — Namespace management (members, settings) +- `notification` — User notifications and inbox +- `promotion` — Skill promotion between scopes +- `publish` — Skill upload/publish UI +- `report` — Skill reporting +- `review` — Review workflow UI +- `search` — Skill search and filtering +- `security-audit` — Security audit viewer +- `skill` — Skill detail, listing, cards +- `social` — Stars, ratings, subscriptions +- `token` — API token management + +### Data Fetching + +- **Always use TanStack Query** (`@tanstack/react-query`) for server state. +- **Never use `useEffect`** for data fetching. +- Use `openapi-fetch` client with generated types from `web/src/api/generated/schema.d.ts`. +- Never use `any` types. + +### State Management + +- **TanStack Query** for server state (API data, caching, invalidation, optimistic updates) +- **Zustand** for local/UI state (theme, sidebar, modals, form state) + +### Component Composition + +- **Radix UI** primitives: `@radix-ui/react-dropdown-menu`, `@radix-ui/react-select` +- **class-variance-authority** (cva) for component variants +- **clsx** + **tailwind-merge** for class merging +- **`cn()` utility**: `web/src/shared/lib/utils.ts` +- **shadcn/ui is NOT used** as a library + +### API Type Generation + +When backend OpenAPI contracts change: + +```bash +make generate-api +``` + +This runs `openapi-typescript http://localhost:8080/v3/api-docs -o src/api/generated/schema.d.ts`. + +Commit the updated `web/src/api/generated/schema.d.ts` with the PR. + +To verify the generated file is not stale: + +```bash +./scripts/check-openapi-generated.sh +``` + +### Styling + +- **Tailwind CSS** for all styling +- **`cn()` utility** for conditional class merging +- Follow existing component patterns in `web/src/shared/components/` + +### Internationalization + +- **i18next** + **react-i18next** for translations +- All user-facing text must be translatable +- Translation keys in `web/src/i18n/` + +### Build & Development + +```bash +make dev-web # Start Vite dev server (HMR enabled) +make build-frontend # Production build +make typecheck-web # TypeScript type check (tsc --noEmit) +make lint-web # ESLint check +make test-frontend # Vitest unit tests +make test-e2e-frontend # Playwright E2E tests +make test-e2e-smoke-frontend # Playwright smoke tests +``` + +Vite HMR is enabled by default — save a file and the browser updates instantly. + +### Frontend Dependencies + +Key dependencies (from `web/package.json`): +- `react` 19, `react-dom` 19 +- `@tanstack/react-query` 5 +- `@tanstack/react-router` 1 +- `@radix-ui/react-dropdown-menu`, `@radix-ui/react-select` +- `class-variance-authority`, `clsx`, `tailwind-merge` +- `openapi-fetch` 0.13 +- `i18next`, `react-i18next` +- `zustand` 5 +- `react-markdown`, `rehype-highlight`, `rehype-sanitize` +- `lucide-react` (icons) +- `sonner` (toasts) + +Build tools: Vite 6, TypeScript 5.7, Vitest 3.2, Playwright 1.58 diff --git a/.agents/skills/pr-submission/SKILL.md b/.agents/skills/pr-submission/SKILL.md new file mode 100644 index 00000000..d1b889af --- /dev/null +++ b/.agents/skills/pr-submission/SKILL.md @@ -0,0 +1,93 @@ +--- +name: pr-submission +description: PR title format, commit conventions, and pre-PR checklist for SkillHub. Use when preparing or reviewing pull requests. +license: Apache-2.0 +--- + +# PR Submission Skill + +## Workflow + +1. Identify the scope of your change (feature, bug fix, docs, test, refactor, chore) +2. Format PR title and commits using the conventions below +3. Run the pre-PR checklist commands +4. Open the PR with a descriptive body + +## PR Title Format + +Use conventional commit style: + +``` +(): +``` + +**Types:** + +| Type | When to Use | +|------|-------------| +| `feat` | New feature or capability | +| `fix` | Bug fix | +| `docs` | Documentation changes only | +| `test` | Adding or updating tests | +| `refactor` | Code restructuring with no behavior change | +| `chore` | Build, CI, tooling, or maintenance tasks | + +**Scopes:** Use module or domain names: `auth`, `search`, `publish`, `review`, `namespace`, `governance`, `deploy`, `ci`, `frontend`, `scanner` + +**Examples:** +``` +feat(auth): add local account login with password reset +fix(publish): resolve null pointer when skill metadata is missing name +docs(deploy): clarify runtime image usage +test(namespace): add membership service edge case tests +refactor(review): extract query repository for governance list +chore(ci): add parallel workflow scripts for multi-agent development +``` + +## Commit Message Format + +Same convention as PR titles. One logical change per commit. + +**Types:** + +- **feat**: A new feature for the user +- **fix**: A bug fix for the user +- **docs**: Documentation changes only +- **test**: Adding or updating tests +- **refactor**: Code change that neither fixes a bug nor adds a feature +- **chore**: Changes to build process, CI, or maintenance tasks + +**Examples:** +``` +fix(auth): resolve session cookie conflict in device flow +feat(publish): support security scan before review submission +docs(skill-protocol): add nested SKILL.md discovery rules +test(search): verify jieba analysis with Chinese skill descriptions +refactor(storage): simplify LocalFile path normalization +``` + +## Pre-PR Checklist + +- [ ] Backend tests pass: `make test-backend-app` +- [ ] Frontend typecheck passes: `make typecheck-web` +- [ ] If API changed: `make generate-api` was run and `web/src/api/generated/schema.d.ts` is committed +- [ ] Smoke test passes: `make staging` +- [ ] Follow existing module boundaries and dependency direction +- [ ] Add/update tests for new behavior +- [ ] Update design docs when APIs, auth flows, deployment, or operator workflows change + +## PR Body Structure + +When creating a PR, include: + +1. **What** — Summary of the change +2. **Why** — Motivation (link to issue if applicable) +3. **How** — Key implementation details (especially for non-obvious decisions) +4. **Testing** — How to verify the change works +5. **Impact** — Breaking changes, migration notes, or rollout considerations + +## Review Conventions + +- When reviewing, cite the specific AGENTS.md rule that applies if suggesting a convention change +- For backend code, check dependency direction does not violate clean architecture rules +- For frontend code, check OpenAPI types are regenerated if API changed diff --git a/.agents/skills/skill-lifecycle/SKILL.md b/.agents/skills/skill-lifecycle/SKILL.md new file mode 100644 index 00000000..8234b479 --- /dev/null +++ b/.agents/skills/skill-lifecycle/SKILL.md @@ -0,0 +1,151 @@ +--- +name: skill-lifecycle +description: The authoritative skill lifecycle state model including container states, version states, review workflow states, visibility overlay, and governance actions. Ensures agents don't introduce invalid states or transitions. +license: Apache-2.0 +--- + +# Skill Lifecycle Skill + +## Trigger + +Use this skill when: +- Modifying skill publish, review, or unpublish flows +- Adding or changing skill/version status fields +- Working on search, detail pages, or listing pages that show skill state +- Implementing governance actions (hide, yank, archive) +- Adding new state transitions or permission checks + +## State Model + +### Skill Container States + +Enum `SkillStatus` (`domain/skill/SkillStatus.java`): + +| Value | Meaning | +|-------|---------| +| `ACTIVE` | Skill is operational and can have versions published | +| `HIDDEN` | Skill hidden by platform governance (design doc says prefer boolean `hidden` flag instead) | +| `ARCHIVED` | Skill archived by owner/namespace admin, cannot publish new versions | + +**Design-vs-code note**: `docs/14-skill-lifecycle.md` specifies `hidden` should be a governance +overlay (boolean flag) rather than a lifecycle enum state. The current code still defines +`SkillStatus.HIDDEN`. New code should use the `skill.hidden` boolean field, not the enum value. + +### SkillVersion States + +Enum `SkillVersionStatus` (`domain/skill/SkillVersionStatus.java`): + +| Value | Meaning | +|-------|---------| +| `DRAFT` | Non-public draft, can resubmit or delete | +| `SCANNING` | Undergoing security scan | +| `SCAN_FAILED` | Security scan failed | +| `UPLOADED` | Uploaded but not yet submitted for review (or withdrawn from review) | +| `PENDING_REVIEW` | Frozen pending reviewer action | +| `PUBLISHED` | Currently distributable | +| `REJECTED` | Review denied, retained | +| `YANKED` | Was published, withdrawn from distribution | + +### ReviewTask States + +Enum `ReviewTaskStatus` (`domain/review/ReviewTaskStatus.java`): + +| Value | Meaning | +|-------|---------| +| `PENDING` | Awaiting reviewer | +| `APPROVED` | Reviewer approved | +| `REJECTED` | Reviewer rejected | + +### Visibility Model + +Enum `SkillVisibility` (used in `SkillPublishService`): + +| Value | Publish Path | +|-------|-------------| +| `PUBLIC` | Creates `PENDING_REVIEW` version, review task, security scan | +| `NAMESPACE_ONLY` | Same as PUBLIC but limited visibility scope | +| `PRIVATE` | Goes directly to `UPLOADED` status, no review task | + +`SUPER_ADMIN` role bypasses review — versions go directly to `PUBLISHED`. + +### Latest Version Pointer + +`Skill.latestVersionId` is **only** the latest published pointer: +- Can only point to a `PUBLISHED` version +- May be `null` if no published version exists +- `latest` tag auto-follows this pointer (read-only) +- When yanking: recalculates to newest remaining `PUBLISHED` version, or `null` + +### Key Transitions + +| Action | From | To | Notes | Source | +|--------|------|-----|-------|--------| +| First upload (PUBLIC/NAMESPACE_ONLY) | — | `PENDING_REVIEW` | Review task created | `SkillPublishService` | +| First upload (SUPER_ADMIN) | — | `PUBLISHED` | Direct publish, `SkillPublishedEvent` emitted | `SkillPublishService` | +| First upload (PRIVATE) | — | `UPLOADED` | No review task, `latestVersionId` updated | `SkillPublishService` | +| Review approve | `PENDING_REVIEW` | `PUBLISHED` | Updates `latestVersionId` | Review workflow | +| Review reject | `PENDING_REVIEW` | `REJECTED` | Version retained | Review workflow | +| Withdraw review | `PENDING_REVIEW` | `UPLOADED` | Deletes pending `ReviewTask` | `SkillGovernanceService.withdrawPendingVersion` | +| Yank | `PUBLISHED` | `YANKED` | Recalculates `latestVersionId` | `SkillGovernanceService.yankVersion` | +| Hide | — | `hidden=true` | Independent overlay | `SkillGovernanceService.hideSkill` | +| Restore | — | `hidden=false` | Independent overlay | `SkillGovernanceService.unhideSkill` | +| Archive | `ACTIVE` | `ARCHIVED` | `SkillStatusChangedEvent` emitted | `SkillGovernanceService.archiveSkill` | +| Unarchive | `ARCHIVED` | `ACTIVE` | `SkillStatusChangedEvent` emitted | `SkillGovernanceService.unarchiveSkill` | +| New publish (existing pending) | `PENDING_REVIEW` | `UPLOADED` | Auto-withdraw + delete review task | `SkillPublishService` | +| Delete version | `DRAFT`/`REJECTED`/`SCAN_FAILED`/`UPLOADED` | — | Last version protected | `SkillGovernanceService.deleteVersion` | + +### Yank Pointer Recalculation + +When yanking the current `latestVersionId` (`SkillGovernanceService`): +1. Query all remaining `PUBLISHED` versions for the skill +2. Sort by `publishedAt` DESC, then `createdAt` DESC, then `id` DESC +3. Point `latestVersionId` to the top result, or `null` if none remain + +### Lifecycle Projection + +Read models (detail, my-skills, favorites, search) use `*QueryRepository` patterns: +- `headlineVersion` — Main display version for the page +- `publishedVersion` — Latest published version +- `ownerPreviewVersion` — Pending review version (visible to owner/namespace admin) +- `resolutionMode` — `PUBLISHED`, `OWNER_PREVIEW`, or `NONE` + +**Public browsing, install, download, search only use `publishedVersion`.** + +### Permission Boundaries + +| Action | Who | +|--------|-----| +| Withdraw review | Submitter only | +| Delete version | Owner or namespace admin, only `DRAFT`/`REJECTED`/`SCAN_FAILED`/`UPLOADED` | +| Archive/unarchive | Owner or namespace admin (`ADMIN` or `OWNER` role) | +| Hide/restore | Platform governance (no permission check in code) | +| Yank | Platform governance (no permission check in code) | +| Publish PUBLIC skill | Namespace member (or `SUPER_ADMIN`) | +| Publish PRIVATE skill | Namespace member (or `SUPER_ADMIN`) | + +### Delete Version Constraints + +`SkillGovernanceService.deleteVersion` enforces: +- Only `DRAFT`, `REJECTED`, `SCAN_FAILED`, or `UPLOADED` versions can be deleted +- Cannot delete the last remaining version of a skill +- Deletes associated storage keys (individual files + `bundle.zip`) +- Deletes associated security scan records +- Updates `latestVersionId` if the deleted version was the pointer +- Storage deletion happens after transaction commit with compensation recording + +### Domain Events + +| Event | When Emitted | +|-------|-------------| +| `SkillStatusChangedEvent` | Archive or unarchive | +| `SkillPublishedEvent` | SUPER_ADMIN direct publish | +| `SkillVersionYankedEvent` | Yank action | +| `ReviewSubmittedEvent` | Create review task for PUBLIC/NAMESPACE_ONLY | + +### Common Pitfalls + +- Setting `SkillStatus.HIDDEN` directly — use `skill.setHidden(true)` via `SkillGovernanceService` instead +- Forgetting to recalculate `latestVersionId` after yank or version deletion +- Not auto-withdrawing pending versions when publishing a new version +- Missing the `confirmWarnings` two-step publish flow (warnings require explicit confirmation) +- Assuming all publish flows create review tasks — `PRIVATE` visibility skips review diff --git a/.agents/skills/testing-and-ci/SKILL.md b/.agents/skills/testing-and-ci/SKILL.md new file mode 100644 index 00000000..d08a3669 --- /dev/null +++ b/.agents/skills/testing-and-ci/SKILL.md @@ -0,0 +1,117 @@ +--- +name: testing-and-ci +description: Testing conventions, CI pipeline rules, and smoke test coverage for SkillHub. Ensures agents write tests correctly and understand the CI gate requirements. +license: Apache-2.0 +--- + +# Testing and CI Skill + +## Trigger + +Use this skill when: +- Adding or modifying backend tests +- Adding or modifying frontend tests +- Changing CI/CD workflows +- Adding smoke tests or E2E tests + +## Rules + +### Backend Testing + +Tests live alongside source in each module's `src/test/java/`: +- `server/skillhub-app/src/test/java/` — Controller integration tests, service tests +- `server/skillhub-domain/src/test/java/` — Domain service unit tests +- `server/skillhub-auth/src/test/java/` — Auth flow tests + +**Tools**: JUnit 5 + Mockito + AssertJ + Spring Boot test slices (`@WebMvcTest`, `@DataJpaTest`) + +**Build commands:** +```bash +make test-backend-app # skillhub-app + dependencies (includes -am) +make test-backend # all backend modules +``` + +**Never** run `./mvnw -pl skillhub-app clean test` directly under `server/`. +`skillhub-app` depends on sibling modules, and a standalone clean build can fall back to stale +artifacts from the local Maven repository, surfacing misleading `cannot find symbol` and +signature-mismatch errors. Use `-am`, or the Makefile targets above. + +**Test naming conventions:** +- Controller tests: `{ControllerName}Test.java` (e.g., `SkillControllerTest.java`) +- Service tests: `{ServiceName}Test.java` +- Integration tests: `{FlowName}IntegrationTest.java` +- Security tests: `{ControllerName}SecurityTest.java` + +### Frontend Testing + +**Tools**: Vitest (unit), Playwright (E2E) + +```bash +make test-frontend # Vitest unit tests (pnpm run test) +make test-e2e-frontend # Playwright E2E tests +make test-e2e-smoke-frontend # Playwright smoke tests (subset) +``` + +E2E tests live in `web/e2e/`. + +### Smoke Tests + +Smoke tests validate end-to-end operator workflows against a running backend: + +| Script | Purpose | +|--------|---------| +| `scripts/smoke-test.sh` | Basic API health, auth, label CRUD | +| `scripts/namespace-smoke-test.sh` | Namespace creation, membership, publishing | +| `scripts/governance-smoke-test.sh` | Governance and moderation flows | +| `scripts/promotion-smoke-test.sh` | Skill promotion between scopes | + +When operator-facing workflows change, update the corresponding smoke test. + +### CI Pipeline + +GitHub Actions workflows in `.github/workflows/`: + +| Workflow | Trigger | Purpose | +|----------|---------|---------| +| `pr-tests.yml` | PR | Backend + frontend unit tests | +| `pr-e2e.yml` | PR | E2E smoke tests against staging | +| `pr-batch-test-deploy.yml` | workflow_dispatch | Batch test and deploy | +| `publish-images.yml` | release published / workflow_dispatch | Build and publish Docker images to GHCR | +| `deploy-docs.yml` | push to docs | Deploy documentation site | +| `issue-triage.yml` | issues | Auto-triage incoming issues | +| `issue-backlog-rescore.yml` | cron (every 6h) | Rescore backlog issues | +| `release-notes.yml` | workflow_dispatch | Generate release notes | +| `deepwiki.yml` | release published | Update DeepWiki documentation | +| `claim-issue-reward.yml` | issue_comment | Auto-claim issue rewards | +| `statistic-member-reward.yml` | cron/schedule | Calculate member rewards | + +All workflows live in `.github/workflows/`. Deno scripts for triage, release notes, and rewards +live in `.github/scripts/`. + +### Staging + +Before opening a PR, validate with staging: + +```bash +make staging # Build backend Docker image + frontend static + smoke test +make staging-down # Tear down +SERVICE=web make staging-logs # View Nginx logs +``` + +Staging validates the containerized deployment path: +- Backend: built as Docker image from local source (`Dockerfile.dev`) +- Frontend: built as static files (`pnpm build`), served by Nginx +- Dependencies: same Postgres/Redis/MinIO as local dev + +If staging passes, the environment stays running at: +- Web UI: `http://localhost` +- Backend API: `http://localhost:8080` + +### Pre-PR Testing Checklist + +- [ ] `make test-backend-app` passes +- [ ] `make typecheck-web` passes +- [ ] `make lint-web` passes (if frontend changed) +- [ ] `make staging` passes (full regression) +- [ ] If API changed: `make generate-api` run and generated file committed +- [ ] New behavior has corresponding tests diff --git a/.gitignore b/.gitignore index 9fe89593..3a6c5f5b 100644 --- a/.gitignore +++ b/.gitignore @@ -82,7 +82,6 @@ docs/review/ docs/superpowers/ # Local workspace metadata -AGENTS.md CLAUDE.md # Local config file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..9a96ff3c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,599 @@ +# SkillHub — AGENTS.md + +**SkillHub** is an **enterprise-grade, self-hosted agent skill registry** for publishing, +discovering, and managing reusable skill packages across an organization. It provides a **REST API +backend**, a **React web UI**, a **security scanner**, and a **ClawHub CLI compatibility layer**. + +## Quick Reference + +| Item | Value | +|------------|------------------------------------------------------------| +| Backend | Spring Boot 3.2.3, Java 21, Maven multi-module (7 modules) | +| Frontend | React 19, TypeScript, Vite, pnpm | +| Scanner | Python (FastAPI), port 8000 | +| Database | PostgreSQL 16 (Flyway migrations) | +| Cache | Redis 7 (sessions, distributed locks, idempotency) | +| Storage | LocalFile (dev) / S3/MinIO (prod) | +| Build | `make dev-all` (dev), `make staging` (pre-PR) | +| Docs | `docs/` (design), `document/` (VitePress user guide) | +| CI | GitHub Actions (`.github/workflows/`) | + +## Directory Map + +``` +skillhub/ +├── server/ # Maven multi-module Spring Boot backend +│ ├── skillhub-app/ # Application layer: bootstrap, controllers, assembly +│ │ ├── bootstrap/ # Bootstrap admin & local dev data initializers +│ │ ├── compat/ # ClawHub CLI compatibility layer controllers +│ │ ├── config/ # Spring configuration classes +│ │ ├── controller/ # REST controllers (transport only) +│ │ │ ├── admin/ # Admin controllers (user mgmt, labels, search) +│ │ │ ├── portal/ # Portal controllers (skills, governance, security) +│ │ │ └── support/ # Package extractors (zip, multipart) +│ │ ├── dto/ # Request/response DTOs +│ │ ├── exception/ # Exception handling +│ │ ├── filter/ # Servlet filters (auth context, rate limiting) +│ │ ├── listener/ # Event listeners (notification recipients, etc.) +│ │ ├── metrics/ # Micrometer metrics +│ │ ├── projection/ # Lifecycle projection models +│ │ ├── ratelimit/ # Rate limiting logic +│ │ ├── repository/ # Query repositories (read-model assembly) +│ │ ├── security/ # Security configuration +│ │ ├── service/ # App services (workflow orchestration) +│ │ ├── stream/ # SSE streaming endpoints +│ │ ├── task/ # Background task scheduling +│ │ └── SkillhubApplication.java # Spring Boot entry point +│ │ +│ ├── skillhub-domain/ # Domain layer: entities, rules, services (innermost) +│ │ ├── audit/ # AuditLog entity, repository, service +│ │ ├── auth/ # Password reset entities +│ │ ├── event/ # Domain event classes (SkillPublishedEvent, etc.) +│ │ ├── governance/ # Governance notification service +│ │ ├── idempotency/ # Idempotency records +│ │ ├── label/ # Skill label management +│ │ ├── namespace/ # Namespace, members, roles, policies +│ │ ├── report/ # Skill reporting/governance +│ │ ├── review/ # Review tasks, promotion requests +│ │ ├── security/ # Security scanning domain model +│ │ ├── shared/ # Shared domain utilities +│ │ │ └── exception/ # Domain exceptions (LocalizedDomainException, etc.) +│ │ ├── skill/ # Core skill entities and services +│ │ │ ├── metadata/ # SKILL.md frontmatter parsing +│ │ │ ├── service/ # Skill domain services (publish, query, governance) +│ │ │ └── validation/ # Package validation (SkillPackagePolicy, etc.) +│ │ ├── social/ # Star, rating, subscription entities +│ │ └── user/ # UserAccount, profile moderation +│ │ +│ ├── skillhub-auth/ # Authentication & authorization +│ │ ├── config/ # Spring Security configuration +│ │ ├── device/ # OAuth Device Flow for CLI auth +│ │ ├── identity/ # Identity binding service +│ │ ├── local/ # Local (password) auth +│ │ ├── merge/ # Account merging +│ │ ├── oauth/ # OAuth2 login handlers +│ │ ├── policy/ # Route security policies +│ │ ├── rbac/ # RBAC service and role definitions +│ │ ├── token/ # API token management +│ │ └── user/ # User-related auth services +│ │ +│ ├── skillhub-search/ # Search SPI + PostgreSQL full-text implementation +│ │ ├── postgres/ # PostgresFullTextIndexService, QueryService +│ │ └── service/ # Search SPI interfaces +│ │ +│ ├── skillhub-storage/ # Object storage SPI +│ │ ├── local/ # LocalFileStorageService +│ │ └── s3/ # S3StorageService (AWS SDK v2) +│ │ +│ ├── skillhub-infra/ # Infrastructure: JPA repos, utilities +│ │ └── repository/ # Spring Data JPA repository implementations +│ │ +│ ├── skillhub-notification/ # Notification service (SSE, email) +│ │ ├── domain/ # Notification domain model +│ │ ├── service/ # Notification delivery services +│ │ └── sse/ # SSE endpoint support +│ │ +│ ├── Dockerfile.dev # Dockerfile for staging builds +│ ├── Dockerfile # Production multi-stage build +│ ├── pom.xml # Parent POM (Spring Boot 3.2.3 parent) +│ └── scripts/ +│ └── run-dev-app.sh # Local dev startup script +│ +├── web/ # React frontend (Vite + pnpm) +│ ├── src/ +│ │ ├── api/ # OpenAPI-generated types + fetch client +│ │ │ └── generated/ +│ │ │ └── schema.d.ts # Generated OpenAPI types (CHECKED IN) +│ │ ├── app/ # Router, layout, global providers +│ │ ├── docs/ # In-app documentation pages +│ │ ├── entities/ # Domain entity display logic +│ │ │ ├── skill/ # Skill card, detail components +│ │ │ ├── user/ # User profile components +│ │ │ └── namespace/ # Namespace display components +│ │ ├── features/ # Business feature modules +│ │ │ ├── admin/ # Admin panel features +│ │ │ ├── auth/ # Login, OAuth flows +│ │ │ ├── governance/ # Skill governance actions +│ │ │ ├── namespace/ # Namespace management +│ │ │ ├── notification/ # User notifications +│ │ │ ├── promotion/ # Skill promotion workflows +│ │ │ ├── publish/ # Skill upload/publish UI +│ │ │ ├── report/ # Skill reporting +│ │ │ ├── review/ # Review workflow UI +│ │ │ ├── search/ # Skill search and filtering +│ │ │ ├── security-audit/ # Security audit viewer +│ │ │ ├── skill/ # Skill detail, listing +│ │ │ ├── social/ # Stars, ratings, subscriptions +│ │ │ └── token/ # API token management +│ │ ├── i18n/ # Internationalization +│ │ ├── pages/ # Route-level page components +│ │ ├── shared/ # Shared UI, hooks, utilities +│ │ │ ├── components/ # Reusable UI components +│ │ │ ├── hooks/ # Custom React hooks +│ │ │ ├── lib/ +│ │ │ │ └── utils.ts # cn() class merging utility +│ │ │ └── ui/ # Radix UI-based primitives +│ │ └── types/ # Additional TypeScript types +│ ├── e2e/ # Playwright E2E tests +│ ├── nginx.conf.template # Nginx runtime config template +│ ├── Dockerfile # Multi-stage build (Node → Nginx) +│ └── package.json # Dependencies (React 19, TanStack Query, Radix UI, etc.) +│ +├── scanner/ # Security scanner (Python/FastAPI) +│ ├── docs/ # Scanner documentation +│ ├── examples/ # Example scan inputs/outputs +│ └── Dockerfile # Scanner container build +│ +├── docs/ # Design documents (source of truth) +│ ├── prds/ # Product requirement documents +│ ├── skillhub/ # VitePress user guide source +│ └── superpowers/ # Internal tooling docs +│ +├── document/ # VitePress documentation site (published) +│ ├── docs/ # Markdown documentation +│ ├── src/ # VitePress theme +│ └── i18n/ # Internationalization +│ +├── deploy/k8s/ # Kubernetes manifests (basic) +├── monitoring/ # Prometheus + Grafana stack +├── scripts/ # Build, test, and deployment scripts +│ ├── smoke-test.sh # Basic API smoke test +│ ├── namespace-smoke-test.sh # Namespace workflow smoke test +│ ├── governance-smoke-test.sh # Governance flow smoke test +│ ├── promotion-smoke-test.sh # Promotion flow smoke test +│ ├── check-openapi-generated.sh # Verify OpenAPI SDK is not stale +│ ├── validate-release-config.sh # Validate release env configuration +│ ├── dev-process.sh # Local process manager (PID-based) +│ ├── runtime.sh # Runtime deployment script +│ ├── parallel-init.sh # Parallel worktree initialization +│ ├── parallel-sync.sh # Merge worktrees in integration branch +│ ├── parallel-up.sh # Merge + start dev environment +│ ├── parallel-down.sh # Stop parallel dev environment +│ └── prepare-pr-batch.sh # Batch PR preparation +│ +├── .github/ +│ ├── workflows/ # GitHub Actions CI/CD +│ ├── ISSUE_TEMPLATE/ # Issue templates +│ └── scripts/ # Deno scripts for triage, release notes, rewards +│ +├── AGENTS.md # AI agent rules (this file) +├── .agents/skills/ # Focused AI skill definitions +├── Makefile # Top-level build/test/dev orchestration +├── docker-compose.yml # Local dev dependency services +├── compose.release.yml # Production release compose file +├── CONTRIBUTING.md # Contribution guidelines +├── CODE_OF_CONDUCT.md # Community standards +└── README.md # Project overview +``` + +**Key Locations for Common Tasks:** + +| Task | Where to Look | +|------|---------------| +| Add REST endpoint | `server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/` | +| Add domain entity/service | `server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/` | +| Add auth logic | `server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/` | +| Add search logic | `server/skillhub-search/src/main/java/com/iflytek/skillhub/search/` | +| Add query repository | `server/skillhub-app/src/main/java/com/iflytek/skillhub/repository/` | +| Change RBAC/roles | `server/skillhub-auth/src/main/java/com/iflytek/skillhub/auth/rbac/` | +| Change skill validation | `server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/validation/` | +| Add frontend page | `web/src/pages/` | +| Add frontend feature | `web/src/features/` | +| Add shared component | `web/src/shared/components/` | +| Change API contract | Backend controller → run `make generate-api` → commit generated file | +| Add smoke test | `scripts/` (new `.sh` file) | +| Add E2E test | `web/e2e/` (Playwright) | +| Add backend test | `server/skillhub-*/src/test/java/` (alongside source module) | + +## Critical Rules + +### Do Not Manually Edit Generated Files + +- `web/src/api/generated/schema.d.ts` — regenerated via `make generate-api` +- `document/docs/` — auto-generated user documentation (VitePress) +- `server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/` — some DTOs may be generated + +### After Making Changes + +**Backend changes:** +- Edit Java code → `make dev-server-restart` (local dev) +- Add/modify controller → `make generate-api` to regenerate frontend types +- Add/modify domain service → `make test-backend-app` to verify tests + +**Frontend changes:** +- Edit TypeScript/React → Vite HMR handles reload automatically +- After `make generate-api` → commit updated `web/src/api/generated/schema.d.ts` + +**Always run before PR:** +```bash +make test-backend-app # Backend tests (with dependent modules) +make typecheck-web # Frontend type check +make lint-web # Frontend lint +make staging # Full staging regression + smoke test +``` + +### File-Specific Requirements + +- **Controllers** (`skillhub-app/controller/`) are transport only: extract auth context, + bind request params, wrap responses. No business logic. +- **App Services** (`skillhub-app/service/`) orchestrate workflows. Do not embed complex + read-model assembly here — extract to query repositories. +- **Query Repositories** (`skillhub-app/repository/`) handle read-model joins and presentation + projection. Named like `*QueryRepository`. +- **Domain Services** (`skillhub-domain/*/service/`) contain business rules and state transitions. + Return domain objects, not DTOs. +- **Repository Interfaces** are defined in `skillhub-domain`, implemented in `skillhub-infra`. +- **Domain Exceptions** use `LocalizedDomainException` for user-facing messages with i18n keys. +- **Package-info files** (`package-info.java`) should exist for all packages. + +## Development Workflow + +### Build & Start + +```bash +make dev-all # Start full stack: Postgres, Redis, MinIO, backend, frontend +make dev-all-down # Stop everything +make dev-all-reset # Full reset (clears data volumes) +make dev-status # Check service status +make dev-server-restart # Restart backend after Java changes +``` + +**Access points:** +- Web UI: `http://localhost:3000` +- Backend API: `http://localhost:8080` +- Scanner: `http://localhost:8000` + +**Local mock users** (no password needed): + +| User ID | Role | Header | +|---------|------|--------| +| `local-user` | Regular user | `X-Mock-User-Id: local-user` | +| `local-admin` | Super admin | `X-Mock-User-Id: local-admin` | + +**Bootstrap admin** (password-based, local profile): +- Username: `admin` / Password: `ChangeMe!2026` +- Disable with `BOOTSTRAP_ADMIN_ENABLED=false` + +### Lint & Format + +```bash +# Backend: enforced by Maven build (no separate lint target) +# Frontend: +make lint-web # ESLint check +make typecheck-web # TypeScript check +``` + +### Testing + +```bash +make test-backend-app # Backend unit tests (skillhub-app + dependencies) +make test-backend # All backend module tests +make test-frontend # Frontend unit tests (Vitest) +make test-e2e-frontend # Frontend E2E tests (Playwright) +make test-e2e-smoke-frontend # Frontend E2E smoke tests +./scripts/smoke-test.sh # API smoke test +make namespace-smoke # Namespace workflow smoke test +``` + +### Staging (Pre-PR Regression) + +```bash +make staging # Build backend Docker image + frontend static + smoke test +make staging-down # Tear down +SERVICE=web make staging-logs # View Nginx logs +``` + +Staging validates the containerized deployment path: +- Backend: built as Docker image from local source +- Frontend: built as static files, served by Nginx +- Dependencies: same Postgres/Redis/MinIO as local dev + +### Parallel Agent Workflow + +For parallel development with isolated worktrees: + +```bash +make parallel-init TASK=feature-name +``` + +Creates dedicated Claude, Codex, and integration worktrees as sibling directories. +See `docs/13-parallel-workflow.md` for details. + +## PR Submission + +### PR Title Format + +Use conventional commit style: + +``` +(): +``` + +**Types:** +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation changes +- `test`: Adding or updating tests +- `refactor`: Code restructuring (no behavior change) +- `chore`: Build, CI, or maintenance tasks + +**Scopes:** Module or domain name (e.g., `auth`, `search`, `publish`, `review`, `namespace`) + +**Examples:** +``` +feat(auth): add local account login +fix(publish): resolve null pointer in skill validation +docs(deploy): clarify runtime image usage +test(namespace): add membership service tests +refactor(review): extract query repository for governance list +chore(ci): add parallel workflow scripts +``` + +### Pre-PR Checklist + +- [ ] Backend tests pass: `make test-backend-app` +- [ ] Frontend typecheck passes: `make typecheck-web` +- [ ] If API changed: `make generate-api` was run and `web/src/api/generated/schema.d.ts` is committed +- [ ] Smoke test passes: `make staging` +- [ ] Follow existing module boundaries and dependency direction +- [ ] Add/update tests for new behavior +- [ ] Update docs when APIs, auth flows, deployment, or operator workflows change + +## Core Concepts + +### Backend Clean Architecture + +``` +app → domain, auth, search, storage, infra, notification +infra → domain # implements domain repository interfaces +auth → domain +search → domain +notification → domain +storage → (independent) # pure SPI +``` + +**Design intent**: `skillhub-domain` is the innermost layer. It defines entities, repository +interfaces, and domain services without depending on infra, auth, search, or storage. + +**Code reality**: `skillhub-domain` declares a Maven dependency on `skillhub-storage`, and several +domain services (`SkillHardDeleteService`, `SkillDownloadService`, `SkillPublishService`, +`SkillGovernanceService`, `SkillQueryService`, `SkillStorageDeletionCompensationService`) import +`com.iflytek.skillhub.storage.ObjectStorageService`. This is an existing deviation from the ideal. +New code should avoid adding further cross-module dependencies from domain. + +### Repository / Query Boundary + +When adding new read logic, follow these rules: + +1. **Domain repository ports** (`skillhub-domain`): Aggregate reads, state transitions, rule + evaluation. Used by domain services. +2. **App query repositories** (`com.iflytek.skillhub.repository`): Read-model assembly that joins + multiple sources, presentation projection. Used by controllers and app services. +3. **App services** (`com.iflytek.skillhub.service`): Workflow orchestration. Should express "what + this endpoint does", not "how it assembles DTOs". +4. **Direct SQL / EntityManager**: Only when necessary, with class-level comment explaining why. + +**Do not** add complex read-model assembly logic inside app services. Extract it into a query +repository when it joins multiple sources, does presentation projection, or is reused across services. + +### Skill Lifecycle + +`SkillVersionStatus` values: `DRAFT`, `SCANNING`, `SCAN_FAILED`, `UPLOADED`, `PENDING_REVIEW`, +`PUBLISHED`, `REJECTED`, `YANKED`. + +`SkillStatus` enum values: `ACTIVE`, `HIDDEN`, `ARCHIVED`. + +The design doc (`docs/14-skill-lifecycle.md`) specifies that `hidden` should be treated as a +governance overlay rather than a lifecycle state. The current code still defines +`SkillStatus.HIDDEN` in the enum. Follow the design doc's intent for new code. + +**Key transitions:** +- Normal user first upload → `PENDING_REVIEW` (no initial DRAFT) +- SUPER_ADMIN first upload → `PUBLISHED` (direct publish) +- Review approve → `PENDING_REVIEW` → `PUBLISHED` (updates `latestVersionId`) +- Review reject → `PENDING_REVIEW` → `REJECTED` +- Withdraw review → `PENDING_REVIEW` → `UPLOADED` (also deletes pending review_task) +- Yank → `PUBLISHED` → `YANKED` (must recalculate `latestVersionId`) +- Hide/restore → independent `hidden` flag (governance overlay) +- Archive/Unarchive → `ACTIVE` ↔ `ARCHIVED` (container state) + +### Namespace Coordinate System + +SkillHub uses `@{namespace_slug}/{skill_slug}`: +- `@global/my-skill` — Platform-level public namespace +- `@my-team/my-skill` — Team/department namespace + +ClawHub CLI compatibility maps: +| SkillHub | Canonical Slug | +|----------|---------------| +| `@global/my-skill` | `my-skill` | +| `@team-name/my-skill` | `team-name--my-skill` | + +### Authentication + +- Web: OAuth2 (GitHub) + local password auth +- CLI: OAuth Device Flow (web authorization → CLI credentials) +- Programmatic: API tokens (prefix-based secure hashing) +- Session: Spring Session + Redis + +### RBAC + +Platform roles: `SUPER_ADMIN`, `SKILL_ADMIN`, `USER_ADMIN`, `AUDITOR` +Namespace roles: `OWNER`, `ADMIN`, `MEMBER` + +### Skill Package Protocol + +- Root: `SKILL.md` with YAML frontmatter (`name`, `description` required) +- Allowed extensions (50+ types): `.md`, `.txt`, `.json`, `.yaml`, `.yml`, `.js`, `.ts`, `.py`, + `.sh`, `.png`, `.jpg`, `.svg`, and many more (see `SkillPackagePolicy.ALLOWED_EXTENSIONS`) +- Limits: 10MB per file, 100MB total, 500 files max +- File type signatures validated (PNG magic bytes, SVG content check, etc.) + +### Frontend State Management + +- **TanStack Query** (`@tanstack/react-query`): All server state (API data) +- **Zustand**: Local/UI state (theme, sidebar, modals) +- **Never** use `useEffect` for data fetching + +### Frontend Component Composition + +- **Radix UI** primitives: `@radix-ui/react-dropdown-menu`, `@radix-ui/react-select` +- **class-variance-authority** (cva) for component variants +- **clsx** + **tailwind-merge** for class merging +- **`cn()` utility**: `web/src/shared/lib/utils.ts` +- shadcn/ui is NOT used as a library — only Radix primitives + utility composition + +## Common Patterns + +### Code Style + +**Java:** +- User identity type is always `String` throughout the codebase +- Use Java 21 features (records, pattern matching, virtual threads) +- Follow existing naming patterns in the domain layer +- Error strings for `DomainBadRequestException`, etc. should be clear and actionable + +**TypeScript:** +- Strict mode. No `any` types. +- Use generated OpenAPI types for all API interactions. +- Feature-Sliced Design: place code at the lowest appropriate layer. + +### Testing Philosophy + +- Backend: JUnit 5 + Mockito + AssertJ +- Frontend: Vitest for unit tests, Playwright for E2E +- **Use `make test-backend-app`** (includes `-am` for dependent modules) — never run + `./mvnw -pl skillhub-app clean test` directly, as it can use stale Maven cache artifacts +- Test behaviors, not implementations +- Use Spring Boot test slices where possible (`@WebMvcTest`, `@DataJpaTest`) + +### Frontend Testing + +```bash +make test-frontend # Vitest unit tests +make test-e2e-frontend # Playwright E2E +make test-e2e-smoke-frontend # Playwright smoke (subset of E2E) +``` + +### Logging Conventions + +- **Backend**: SLF4J + Spring Boot logging. Use structured logging with MDC for request tracing. +- **Frontend**: `console.error` for errors, `console.warn` for deprecations, avoid `console.log` in production code. +- **Scanner**: Python logging module with structured JSON output. + +### Security + +- API tokens are stored as prefix-based secure hashes, never in plaintext +- OAuth2 client secrets and other secrets must not be logged or committed +- User identity is `String` (supports external SSO/OIDC/SCIM identity sources) +- The bootstrap admin (`BOOTSTRAP_ADMIN_ENABLED`) is for zero-config quickstart only + +## Search Tips + +```bash +# Find all REST endpoints +rg "@(Get|Post|Put|Delete|Patch)Mapping" --type java + +# Find domain services +rg "class.*Service" server/skillhub-domain/ + +# Find query repositories +rg "QueryRepository" server/skillhub-app/ + +# Find controllers +rg "@RestController" server/skillhub-app/ + +# Find RBAC role checks +rg "@PreAuthorize" server/skillhub-app/ + +# Find skill validation logic +rg "SkillPackage" server/skillhub-domain/ + +# Find frontend features +rg "export" web/src/features/ + +# Find OpenAPI type generation script +rg "generate-api" web/package.json + +# Find event listeners +rg "@EventListener" server/ +``` + +## Design Philosophy + +- **Hub first**: The server-side registry is the core product; CLI and agent integrations are entry capabilities +- **Compatibility first**: Support `SKILL.md` format and common directory conventions +- **Layered architecture**: Search and object storage must have replaceable boundaries (SPI pattern) +- **Open authentication**: OAuth2-based, extensible to multiple providers beyond GitHub +- **Audit first**: Enterprise distribution requires audit trails for publish, download, delete, and authorization + +## References + +### Essential Files +- **`Makefile`** — All build/test/dev automation targets +- **`CONTRIBUTING.md`** — Contribution guidelines and commit style +- **`CODE_OF_CONDUCT.md`** — Community standards +- **`server/pom.xml`** — Maven parent POM, module definitions, dependency versions +- **`web/package.json`** — Frontend dependencies and scripts +- **`.github/workflows/pr-tests.yml`** — PR test pipeline +- **`.github/workflows/publish-images.yml`** — Docker image publish to GHCR + +### Key Directories +- **`server/skillhub-domain/`** — Core domain (entities, services, rules) +- **`server/skillhub-app/controller/`** — REST API endpoints +- **`server/skillhub-app/repository/`** — Query repositories +- **`server/skillhub-app/compat/`** — ClawHub CLI compatibility layer +- **`server/skillhub-auth/`** — Authentication and authorization +- **`web/src/features/`** — Frontend feature modules +- **`web/src/api/generated/`** — Generated OpenAPI types +- **`docs/`** — Design documents +- **`scripts/`** — Build, test, and deployment scripts + +### Important Scripts +- **`scripts/smoke-test.sh`** — Basic API smoke test +- **`scripts/namespace-smoke-test.sh`** — Namespace workflow test +- **`scripts/check-openapi-generated.sh`** — Verify frontend SDK is current +- **`scripts/validate-release-config.sh`** — Validate production env config +- **`scripts/dev-process.sh`** — Local process manager (PID-based lifecycle) +- **`scripts/parallel-init.sh`** — Create isolated worktrees for parallel development + +### Design Documents +- **`00-product-direction.md`** — Product positioning, MVP scope, coordinate system +- **`01-system-architecture.md`** — System architecture, module structure, dependency rules +- **`02-domain-model.md`** — Domain entities and relationships +- **`03-authentication-design.md`** — OAuth2, CLI Device Flow, API tokens +- **`04-search-architecture.md`** — Search SPI and implementations +- **`05-business-flows.md`** — Business process flows +- **`06-api-design.md`** — API contract specifications +- **`07-skill-protocol.md`** — SKILL.md format, package structure, CLI compatibility +- **`08-frontend-architecture.md`** — Frontend patterns and conventions +- **`14-skill-lifecycle.md`** — Skill state model (authoritative) +- **`dev-workflow.md`** — Local development workflow guide + +### External Resources +- **SkillHub Docs**: https://iflytek.github.io/skillhub/ +- **DeepWiki**: https://deepwiki.com/iflytek/skillhub +- **Discord**: https://discord.gg/qHYvtDNPHS +- **OpenSkills**: https://agents.md/ (skill package format reference) +- **OpenClaw**: https://github.com/openclaw/openclaw (CLI compatibility) +- **AstronClaw**: https://agent.xfyun.cn/astron-claw (cloud AI assistant integration) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a382e5d7..2b5b5df3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,10 @@ SkillHub is a self-hosted registry for agent skills. Contributions should preserve the existing architecture and product direction documented in [`docs/`](./docs). +AI coding agents working in this repository should follow the rules in +[`AGENTS.md`](./AGENTS.md), which documents repository architecture, +dependency rules, and agent-specific conventions. + ## Before You Start - Read [`README.md`](./README.md) for local development commands.