mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-27 11:14:59 +00:00
Add AGENTS.md and SKILL.md to support AI tools (#393)
Signed-off-by: dongjiang <dongjiang1989@126.com>
This commit is contained in:
parent
15e55e8055
commit
41b1d03cfc
11 changed files with 1665 additions and 1 deletions
140
.agents/skills/api-and-namespace-design/SKILL.md
Normal file
140
.agents/skills/api-and-namespace-design/SKILL.md
Normal file
|
|
@ -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
|
||||
108
.agents/skills/backend-module-structure/SKILL.md
Normal file
108
.agents/skills/backend-module-structure/SKILL.md
Normal file
|
|
@ -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.
|
||||
135
.agents/skills/code-conventions/SKILL.md
Normal file
135
.agents/skills/code-conventions/SKILL.md
Normal file
|
|
@ -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
|
||||
194
.agents/skills/dev-workflow/SKILL.md
Normal file
194
.agents/skills/dev-workflow/SKILL.md
Normal file
|
|
@ -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:
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
```
|
||||
|
||||
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 |
|
||||
124
.agents/skills/frontend-conventions/SKILL.md
Normal file
124
.agents/skills/frontend-conventions/SKILL.md
Normal file
|
|
@ -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
|
||||
93
.agents/skills/pr-submission/SKILL.md
Normal file
93
.agents/skills/pr-submission/SKILL.md
Normal file
|
|
@ -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:
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
```
|
||||
|
||||
**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
|
||||
151
.agents/skills/skill-lifecycle/SKILL.md
Normal file
151
.agents/skills/skill-lifecycle/SKILL.md
Normal file
|
|
@ -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
|
||||
117
.agents/skills/testing-and-ci/SKILL.md
Normal file
117
.agents/skills/testing-and-ci/SKILL.md
Normal file
|
|
@ -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
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -82,7 +82,6 @@ docs/review/
|
|||
docs/superpowers/
|
||||
|
||||
# Local workspace metadata
|
||||
AGENTS.md
|
||||
CLAUDE.md
|
||||
|
||||
# Local config file
|
||||
|
|
|
|||
599
AGENTS.md
Normal file
599
AGENTS.md
Normal file
|
|
@ -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:
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
```
|
||||
|
||||
**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)
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue