diff --git a/frontend/.cta.json b/frontend/.cta.json
index 9fab49b..6c3ed64 100644
--- a/frontend/.cta.json
+++ b/frontend/.cta.json
@@ -22,4 +22,4 @@
"t3env",
"tanstack-query"
]
-}
\ No newline at end of file
+}
diff --git a/frontend/README.md b/frontend/README.md
index 3b73340..b7dad78 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -32,7 +32,6 @@ If you prefer not to use Tailwind CSS:
## Linting & Formatting
-
This project uses [eslint](https://eslint.org/) and [prettier](https://prettier.io/) for linting and formatting. Eslint is configured using [tanstack/eslint-config](https://tanstack.com/config/latest/docs/eslint). The following scripts are available:
```bash
@@ -41,7 +40,6 @@ bun --bun run format
bun --bun run check
```
-
## Deploy with Nitro
This project uses Nitro as a generic server adapter, so it can run on any Node-compatible host.
@@ -55,7 +53,6 @@ The build output is a self-contained Node server. To deploy, push the `dist/` di
For host-specific presets (Vercel, Netlify, Cloudflare, AWS Lambda, etc.) and tuning, see https://v3.nitro.build/deploy.
-
## Shadcn
Add components using the latest version of [Shadcn](https://ui.shadcn.com/).
@@ -64,7 +61,6 @@ Add components using the latest version of [Shadcn](https://ui.shadcn.com/).
pnpm dlx shadcn@latest add button
```
-
## T3Env
- You can use T3Env to add type safety to your environment variables.
@@ -74,16 +70,11 @@ pnpm dlx shadcn@latest add button
### Usage
```ts
-import { env } from "#/env";
+import { env } from '#/env'
-console.log(env.VITE_APP_TITLE);
+console.log(env.VITE_APP_TITLE)
```
-
-
-
-
-
## Routing
This project uses [TanStack Router](https://tanstack.com/router) with file-based routing. Routes are managed as files in `src/routes`.
@@ -101,7 +92,7 @@ Now that you have two routes you can use a `Link` component to navigate between
To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
```tsx
-import { Link } from "@tanstack/react-router";
+import { Link } from '@tanstack/react-router'
```
Then anywhere in your JSX you can use it like so:
@@ -169,11 +160,11 @@ const getServerTime = createServerFn({
// Use in a component
function MyComponent() {
const [time, setTime] = useState('')
-
+
useEffect(() => {
getServerTime().then(setTime)
}, [])
-
+
return
Server time: {time}
}
```
@@ -226,8 +217,6 @@ function PeopleComponent() {
Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
-
-
# Learn More
You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
diff --git a/frontend/bun.lock b/frontend/bun.lock
index 1a6d2be..cf95977 100644
--- a/frontend/bun.lock
+++ b/frontend/bun.lock
@@ -5,6 +5,7 @@
"": {
"name": "frontend",
"dependencies": {
+ "@microsoft/fetch-event-source": "^2.0.1",
"@t3-oss/env-core": "^0.13.10",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-devtools": "latest",
@@ -133,6 +134,8 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+ "@microsoft/fetch-event-source": ["@microsoft/fetch-event-source@2.0.1", "", {}, "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA=="],
+
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.0", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^2.0.0-alpha.3", "@emnapi/runtime": "^2.0.0-alpha.3" } }, "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA=="],
"@oozcitak/dom": ["@oozcitak/dom@2.0.2", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/url": "^3.0.0", "@oozcitak/util": "^10.0.0" } }, "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w=="],
diff --git a/frontend/components.json b/frontend/components.json
index cdcadc6..323cfb2 100644
--- a/frontend/components.json
+++ b/frontend/components.json
@@ -18,4 +18,4 @@
"hooks": "#/hooks"
},
"iconLibrary": "lucide"
-}
\ No newline at end of file
+}
diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js
index 3f272c0..ae58115 100644
--- a/frontend/eslint.config.js
+++ b/frontend/eslint.config.js
@@ -15,6 +15,6 @@ export default [
},
},
{
- ignores: ['eslint.config.js', 'prettier.config.js'],
+ ignores: ['eslint.config.js', 'prettier.config.js', '.output/**'],
},
]
diff --git a/frontend/package.json b/frontend/package.json
index d600c48..a23c85c 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -15,6 +15,7 @@
"check": "prettier --check ."
},
"dependencies": {
+ "@microsoft/fetch-event-source": "^2.0.1",
"@t3-oss/env-core": "^0.13.10",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/react-devtools": "latest",
@@ -62,4 +63,4 @@
"lightningcss"
]
}
-}
\ No newline at end of file
+}
diff --git a/frontend/prettier.config.js b/frontend/prettier.config.js
index aea1c48..f7279f7 100644
--- a/frontend/prettier.config.js
+++ b/frontend/prettier.config.js
@@ -4,7 +4,7 @@
const config = {
semi: false,
singleQuote: true,
- trailingComma: "all",
-};
+ trailingComma: 'all',
+}
-export default config;
+export default config
diff --git a/frontend/specs/auth.md b/frontend/specs/auth.md
index fed0099..377624c 100644
--- a/frontend/specs/auth.md
+++ b/frontend/specs/auth.md
@@ -15,11 +15,11 @@ security, cookie, and exception behavior is under `com.meet.server.common`.
restated as a controller contract here.
- Successful controller responses use `ApiResponse`:
- | Field | Type | Meaning |
- |---|---|---|
- | `success` | `boolean` | Whether the operation succeeded. |
- | `message` | `string` | Human-readable result message. |
- | `data` | `Optional` | Response payload; logout uses an empty value. |
+ | Field | Type | Meaning |
+ | --------- | ------------- | --------------------------------------------- |
+ | `success` | `boolean` | Whether the operation succeeded. |
+ | `message` | `string` | Human-readable result message. |
+ | `data` | `Optional` | Response payload; logout uses an empty value. |
- Validation and application errors are also returned through `ApiResponse` by `GlobalExceptionHandler`.
- The refresh token is stored in an HTTP-only `refresh_token` cookie with `SameSite=Lax`, `Path=/`, and a seven-day
@@ -35,27 +35,32 @@ security, cookie, and exception behavior is under `com.meet.server.common`.
- Authentication/authorization: Public (`permitAll`).
- Request body: `RegisterRequest`.
-| Field | Type | Required | Constraints |
-|---|---|---:|---|
-| `fullName` | `string` | Yes | `@NotBlank`, maximum 100 characters |
-| `username` | `string` | Yes | `@NotBlank`, maximum 50 characters |
-| `email` | `string` | Yes | `@NotBlank`, `@Email`, maximum 255 characters |
-| `password` | `string` | Yes | `@NotBlank`, 8–100 characters |
+| Field | Type | Required | Constraints |
+| ---------- | -------- | -------: | --------------------------------------------- |
+| `fullName` | `string` | Yes | `@NotBlank`, maximum 100 characters |
+| `username` | `string` | Yes | `@NotBlank`, maximum 50 characters |
+| `email` | `string` | Yes | `@NotBlank`, `@Email`, maximum 255 characters |
+| `password` | `string` | Yes | `@NotBlank`, 8–100 characters |
Example:
```json
-{"fullName":"Jane Doe","username":"jane_doe","email":"jane@example.com","password":"Str0ngPass!"}
+{
+ "fullName": "Jane Doe",
+ "username": "jane_doe",
+ "email": "jane@example.com",
+ "password": "Str0ngPass!"
+}
```
Responses:
-| Status | Body |
-|---|---|
-| `200 OK` | `ApiResponse` with message `Registration successful`; also sets `refresh_token`. |
-| `400 Bad Request` | Validation error envelope containing field/object messages. |
-| `409 Conflict` | `ApiResponse` with the duplicate-email or duplicate-username message. |
-| `500 Internal Server Error` | Generic `ApiResponse` with message `An unexpected error occurred`. |
+| Status | Body |
+| --------------------------- | ---------------------------------------------------------------------------------------------- |
+| `200 OK` | `ApiResponse` with message `Registration successful`; also sets `refresh_token`. |
+| `400 Bad Request` | Validation error envelope containing field/object messages. |
+| `409 Conflict` | `ApiResponse` with the duplicate-email or duplicate-username message. |
+| `500 Internal Server Error` | Generic `ApiResponse` with message `An unexpected error occurred`. |
Domain conflict codes are `EMAIL_ALREADY_EXISTS` and `USERNAME_ALREADY_EXISTS`.
@@ -65,25 +70,25 @@ Domain conflict codes are `EMAIL_ALREADY_EXISTS` and `USERNAME_ALREADY_EXISTS`.
- Authentication/authorization: Public (`permitAll`).
- Request body: `LoginRequest`.
-| Field | Type | Required | Constraints |
-|---|---|---:|---|
-| `email` | `string` | Yes | `@NotBlank`, `@Email` |
-| `password` | `string` | Yes | `@NotBlank` |
+| Field | Type | Required | Constraints |
+| ---------- | -------- | -------: | --------------------- |
+| `email` | `string` | Yes | `@NotBlank`, `@Email` |
+| `password` | `string` | Yes | `@NotBlank` |
Example:
```json
-{"email":"jane@example.com","password":"Str0ngPass!"}
+{ "email": "jane@example.com", "password": "Str0ngPass!" }
```
Responses:
-| Status | Body |
-|---|---|
-| `200 OK` | `ApiResponse` with message `Login successful`; also sets `refresh_token`. |
-| `400 Bad Request` | Validation error envelope containing field/object messages. |
-| `401 Unauthorized` | `ApiResponse` with `Invalid email or password`. |
-| `500 Internal Server Error` | Generic `ApiResponse`. |
+| Status | Body |
+| --------------------------- | --------------------------------------------------------------------------------------- |
+| `200 OK` | `ApiResponse` with message `Login successful`; also sets `refresh_token`. |
+| `400 Bad Request` | Validation error envelope containing field/object messages. |
+| `401 Unauthorized` | `ApiResponse` with `Invalid email or password`. |
+| `500 Internal Server Error` | Generic `ApiResponse`. |
### POST /api/auth/refresh
@@ -91,20 +96,20 @@ Responses:
- Authentication/authorization: Public (`permitAll`).
- Request cookie:
- | Name | Type | Required | Constraints |
- |---|---|---:|---|
- | `refresh_token` | opaque `string` | Yes | `@CookieValue(required=true)` |
+ | Name | Type | Required | Constraints |
+ | --------------- | --------------- | -------: | ----------------------------- |
+ | `refresh_token` | opaque `string` | Yes | `@CookieValue(required=true)` |
- Request body: None.
Responses:
-| Status | Body |
-|---|---|
-| `200 OK` | `ApiResponse` with message `Token refreshed`; sets a rotated `refresh_token`. |
-| `400 Bad Request` | `ApiResponse` with message `Malformed or incomplete request` when the required cookie is absent. |
-| `401 Unauthorized` | `ApiResponse` containing an invalid, expired, revoked, required, or reused-token message. |
-| `500 Internal Server Error` | Generic `ApiResponse`. |
+| Status | Body |
+| --------------------------- | ------------------------------------------------------------------------------------------------------ |
+| `200 OK` | `ApiResponse` with message `Token refreshed`; sets a rotated `refresh_token`. |
+| `400 Bad Request` | `ApiResponse` with message `Malformed or incomplete request` when the required cookie is absent. |
+| `401 Unauthorized` | `ApiResponse` containing an invalid, expired, revoked, required, or reused-token message. |
+| `500 Internal Server Error` | Generic `ApiResponse`. |
Token messages defined by `RefreshTokenService` include `Refresh token is required`, `Invalid refresh token`,
`Refresh token expired`, `Refresh token is revoked`, and `Refresh token reuse detected. All sessions invalidated.`
@@ -115,21 +120,21 @@ Token messages defined by `RefreshTokenService` include `Refresh token is requir
- Authentication/authorization: Public (`permitAll`).
- Request parameters:
- | Name | Location | Type | Required | Meaning |
- |---|---|---|---:|---|
- | `refresh_token` | Cookie | `string` | No | If present, revoke the token owner's sessions. |
- | `authentication` | Security context | `Authentication` | No | Used when no refresh cookie exists; its name is parsed as a UUID. |
+ | Name | Location | Type | Required | Meaning |
+ | ---------------- | ---------------- | ---------------- | -------: | ----------------------------------------------------------------- |
+ | `refresh_token` | Cookie | `string` | No | If present, revoke the token owner's sessions. |
+ | `authentication` | Security context | `Authentication` | No | Used when no refresh cookie exists; its name is parsed as a UUID. |
- Request body: None.
Responses:
-| Status | Body |
-|---|---|
-| `200 OK` | `ApiResponse` with message `Logout successful`, empty `data`, and a cleared `refresh_token` cookie. |
-| `400 Bad Request` | Malformed/incomplete request envelope where applicable. |
-| `401 Unauthorized` | Invalid refresh-token failure when revocation is attempted. |
-| `500 Internal Server Error` | Generic `ApiResponse` for unhandled failures, including UUID parsing failures. |
+| Status | Body |
+| --------------------------- | --------------------------------------------------------------------------------------------------------- |
+| `200 OK` | `ApiResponse` with message `Logout successful`, empty `data`, and a cleared `refresh_token` cookie. |
+| `400 Bad Request` | Malformed/incomplete request envelope where applicable. |
+| `401 Unauthorized` | Invalid refresh-token failure when revocation is attempted. |
+| `500 Internal Server Error` | Generic `ApiResponse` for unhandled failures, including UUID parsing failures. |
### GET /api/auth/me
@@ -140,12 +145,12 @@ Responses:
Responses:
-| Status | Body |
-|---|---|
-| `200 OK` | `ApiResponse` with message `Current user retrieved`. |
-| `401 Unauthorized` | `ApiResponse` with `Unauthorized`, written by `UnauthorizedResponseHandler`. |
-| `404 Not Found` | `ApiResponse` with `User not found`. |
-| `500 Internal Server Error` | Generic `ApiResponse` for unhandled failures. |
+| Status | Body |
+| --------------------------- | ---------------------------------------------------------------------------------- |
+| `200 OK` | `ApiResponse` with message `Current user retrieved`. |
+| `401 Unauthorized` | `ApiResponse` with `Unauthorized`, written by `UnauthorizedResponseHandler`. |
+| `404 Not Found` | `ApiResponse` with `User not found`. |
+| `500 Internal Server Error` | Generic `ApiResponse` for unhandled failures. |
### GET /oauth2/authorization/{registrationId}
@@ -153,9 +158,9 @@ Responses:
- Authentication/authorization: Public through `/oauth2/**`.
- Path parameter:
- | Name | Type | Required | Values |
- |---|---|---:|---|
- | `registrationId` | `string` | Yes | Configured registrations: `google`, `github`. |
+ | Name | Type | Required | Values |
+ | ---------------- | -------- | -------: | --------------------------------------------- |
+ | `registrationId` | `string` | Yes | Configured registrations: `google`, `github`. |
- Request body: None.
- Response: Framework-generated redirect to the selected provider. Exact status and provider URL are not specified in
@@ -189,21 +194,21 @@ No auth-feature WebSocket/STOMP handlers or listeners were found in source.
### AuthResponse
-| Field | Type | Required | Nullable | Meaning |
-|---|---|---:|---:|---|
-| `accessToken` | `string` | Yes | Not specified | Generated JWT access token. |
-| `user` | `UserResponse` | Yes | Not specified | Public user profile. |
+| Field | Type | Required | Nullable | Meaning |
+| ------------- | -------------- | -------: | ------------: | --------------------------- |
+| `accessToken` | `string` | Yes | Not specified | Generated JWT access token. |
+| `user` | `UserResponse` | Yes | Not specified | Public user profile. |
### UserResponse
-| Field | Type | Required | Nullable | Meaning |
-|---|---|---:|---:|---|
-| `id` | `UUID` | Yes | Not specified | User identifier. |
-| `fullName` | `string` | Yes | Not specified | Display name. |
-| `username` | `string` | Yes | Not specified | Username. |
-| `email` | `string` | Yes | Not specified | Email address. |
-| `avatarUrl` | `string` | Yes | Not specified | Profile image URL; provider data may be absent. |
-| `role` | `UserRole` | Yes | Not specified | `USER` or `ADMIN`. |
+| Field | Type | Required | Nullable | Meaning |
+| ----------- | ---------- | -------: | ------------: | ----------------------------------------------- |
+| `id` | `UUID` | Yes | Not specified | User identifier. |
+| `fullName` | `string` | Yes | Not specified | Display name. |
+| `username` | `string` | Yes | Not specified | Username. |
+| `email` | `string` | Yes | Not specified | Email address. |
+| `avatarUrl` | `string` | Yes | Not specified | Profile image URL; provider data may be absent. |
+| `role` | `UserRole` | Yes | Not specified | `USER` or `ADMIN`. |
### Error envelopes
@@ -214,28 +219,28 @@ and `data` mapping field/property names to validation messages. `MethodArgumentN
Custom application errors are returned as `ApiResponse` with `success=false`, the exception message, and empty
`data`:
-| Exception | Status |
-|---|---|
-| `AuthException` | Status carried by the exception, including `401`, `404`, and `409`. |
-| `InvalidTokenException` | `401 Unauthorized`. |
-| `ResponseStatusException` | Status carried by the exception. |
-| Malformed request or missing required parameter | `400 Bad Request`. |
-| Unhandled `Exception` | `500 Internal Server Error`. |
+| Exception | Status |
+| ----------------------------------------------- | ------------------------------------------------------------------- |
+| `AuthException` | Status carried by the exception, including `401`, `404`, and `409`. |
+| `InvalidTokenException` | `401 Unauthorized`. |
+| `ResponseStatusException` | Status carried by the exception. |
+| Malformed request or missing required parameter | `400 Bad Request`. |
+| Unhandled `Exception` | `500 Internal Server Error`. |
The `AuthException.errorCode` values are application metadata (`EMAIL_ALREADY_EXISTS`, `USERNAME_ALREADY_EXISTS`,
`INVALID_CREDENTIALS`, `OAUTH_EMAIL_MISSING`, and `USER_NOT_FOUND`) but are not serialized by the current handler.
### Refresh-token cookie
-| Property | Value |
-|---|---|
-| Name | `refresh_token` |
-| HTTP-only | `true` |
-| SameSite | `Lax` |
-| Path | `/` |
-| Secure | `true` except when `app.env=dev` |
-| Max-Age when issued | `604800` seconds (7 days) |
-| Max-Age when cleared | `0` |
+| Property | Value |
+| -------------------- | -------------------------------- |
+| Name | `refresh_token` |
+| HTTP-only | `true` |
+| SameSite | `Lax` |
+| Path | `/` |
+| Secure | `true` except when `app.env=dev` |
+| Max-Age when issued | `604800` seconds (7 days) |
+| Max-Age when cleared | `0` |
## Source references
diff --git a/frontend/specs/codebase.md b/frontend/specs/codebase.md
new file mode 100644
index 0000000..bf6b017
--- /dev/null
+++ b/frontend/specs/codebase.md
@@ -0,0 +1,261 @@
+# Codebase API Specification
+
+## Overview
+
+The codebase feature manages authenticated repository imports, indexing, metadata updates, deletion, reindexing, and
+code-aware chat over indexed repositories. The primary source modules are
+`com.meet.server.feature.codebase` and `com.meet.server.feature.chat`.
+
+## Authentication and common conventions
+
+- All endpoints require authentication because `SecurityConfig` applies `.anyRequest().authenticated()`.
+- The authenticated user ID is read from `Authentication.getName()` and parsed as a UUID.
+- JSON endpoints use `ApiResponse` with `success`, `message`, and `data` fields.
+- Validation and domain failures are mapped by `GlobalExceptionHandler` to the same response envelope.
+- Codebase IDs are UUIDs.
+- The list endpoint is not paginated in the current source.
+- Chat uses `text/event-stream` SSE and does not use `ApiResponse` for the stream itself.
+
+## HTTP endpoints
+
+### POST /api/codebases
+
+- Purpose: Queue a repository import and indexing job.
+- Authentication/authorization: Authenticated user required. The new codebase belongs to that user.
+- Request headers/path/query parameters:
+
+| Name | Type | Required | Constraints | Description |
+|---|---|---:|---|---|
+| `Authorization` | string | Yes | Authentication is enforced by security configuration. | Authenticated principal supplies the user ID. |
+
+- Request body: `CodebaseImportRequest`
+
+| Field | Type | Required | Nullability | Constraints | Description |
+|---|---|---:|---|---|---|
+| `name` | string | Yes | Non-null | `@NotBlank` | Display name. |
+| `cloneUrl` | string | Yes | Non-null | `@NotBlank`; HTTPS pattern; service requires HTTPS, a host, and no user info. | Repository clone URL. |
+| `branch` | string | No | Nullable | Blank or null becomes `main`. | Branch to clone. |
+
+Example:
+
+```json
+{
+ "name": "Server Repo",
+ "cloneUrl": "https://github.com/example/server.git",
+ "branch": "main"
+}
+```
+
+- Responses:
+
+| Status | Body | Example |
+|---|---|---|
+| `202 Accepted` | `ApiResponse` | `{"success":true,"message":"Codebase import queued","data":{"codebaseId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"QUEUED","fileCount":0}}` |
+| `400 Bad Request` | Validation or clone URL error envelope | `{"success":false,"message":"Validation failed","data":{"cloneUrl":"cloneUrl must use HTTPS"}}` |
+| `409 Conflict` | `ApiResponse` | `{"success":false,"message":"A user can have at most 5 codebases","data":null}` |
+
+- Errors:
+ - `CODEBASE_LIMIT_REACHED` (`409`) when the user already has five persisted codebases. All statuses count.
+ - `INVALID_CLONE_URL` (`400`) when service-level URI validation fails.
+ - Validation failures (`400`) for invalid request fields.
+
+### GET /api/codebases
+
+- Purpose: Return all codebases owned by the authenticated user.
+- Authentication/authorization: Authenticated user required; results are filtered by the authenticated user ID.
+- Request headers/path/query parameters: No path, query, or body parameters. The `Authorization` header is required by security configuration.
+- Responses:
+
+| Status | Body | Example |
+|---|---|---|
+| `200 OK` | `ApiResponse>` | `{"success":true,"message":"Codebases retrieved","data":[{"codebaseId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","name":"Server Repo","cloneUrl":"https://github.com/example/server.git","branch":"main","status":"INDEXED","lastCommitSha":"abc123","indexedAt":"2026-08-05T10:00:00Z","createdAt":"2026-08-05T09:00:00Z","updatedAt":"2026-08-05T10:00:00Z","fileCount":12}]}` |
+
+The repository query aggregates file counts in the list query; pagination is not specified in source.
+
+### PATCH /api/codebases/{codebaseId}
+
+- Purpose: Update codebase metadata without reindexing.
+- Authentication/authorization: Authenticated user required; the codebase must belong to the authenticated user.
+- Request headers/path/query parameters:
+
+| Name | Type | Required | Constraints | Description |
+|---|---|---:|---|---|
+| `Authorization` | string | Yes | Authentication is enforced by security configuration. | Authenticated principal. |
+| `codebaseId` | UUID | Yes | Valid UUID path value. | Target codebase. |
+
+- Request body: `CodebaseUpdateRequest`
+
+| Field | Type | Required | Nullability | Constraints | Description |
+|---|---|---:|---|---|---|
+| `name` | string | Yes | Non-null | `@NotBlank` | New display name. |
+| `branch` | string | Yes | Non-null | `@NotBlank` | New branch name. |
+
+`cloneUrl` cannot be updated by this endpoint.
+
+- Responses:
+
+| Status | Body | Example |
+|---|---|---|
+| `200 OK` | `ApiResponse` | `{"success":true,"message":"Codebase updated","data":{"codebaseId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","name":"Updated Repo","cloneUrl":"https://github.com/example/server.git","branch":"develop","status":"INDEXED","lastCommitSha":"abc123","indexedAt":"2026-08-05T10:00:00Z","createdAt":"2026-08-05T09:00:00Z","updatedAt":"2026-08-05T10:05:00Z","fileCount":12}}` |
+| `400 Bad Request` | Validation error envelope | `{"success":false,"message":"Validation failed","data":{"name":"must not be blank"}}` |
+| `403 Forbidden` | `ApiResponse` | `{"success":false,"message":"You do not own this codebase","data":null}` |
+| `404 Not Found` | `ApiResponse` | `{"success":false,"message":"Codebase not found","data":null}` |
+
+### DELETE /api/codebases/{codebaseId}
+
+- Purpose: Permanently delete the codebase and its indexed repository files and chunks.
+- Authentication/authorization: Authenticated user required; the codebase must belong to the authenticated user.
+- Request headers/path/query parameters:
+
+| Name | Type | Required | Constraints | Description |
+|---|---|---:|---|---|
+| `Authorization` | string | Yes | Authentication is enforced by security configuration. | Authenticated principal. |
+| `codebaseId` | UUID | Yes | Valid UUID path value. | Target codebase. |
+
+- Request body: None.
+- Responses:
+
+| Status | Body | Description |
+|---|---|---|
+| `204 No Content` | Empty body | Codebase and child indexed data were deleted. |
+| `403 Forbidden` | `ApiResponse` | Authenticated user does not own the codebase. |
+| `404 Not Found` | `ApiResponse` | Codebase does not exist. |
+| `409 Conflict` | `ApiResponse` | Codebase is `QUEUED` or `PROCESSING`. |
+
+### POST /api/codebases/{codebaseId}/reindex
+
+- Purpose: Remove existing indexed data and queue a fresh clone/index operation.
+- Authentication/authorization: Authenticated user required; the codebase must belong to the authenticated user.
+- Request headers/path/query parameters:
+
+| Name | Type | Required | Constraints | Description |
+|---|---|---:|---|---|
+| `Authorization` | string | Yes | Authentication is enforced by security configuration. | Authenticated principal. |
+| `codebaseId` | UUID | Yes | Valid UUID path value. | Target codebase. |
+
+- Request body: None.
+- Responses:
+
+| Status | Body | Example |
+|---|---|---|
+| `202 Accepted` | `ApiResponse` | `{"success":true,"message":"Codebase reindex queued","data":{"codebaseId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"QUEUED","fileCount":0}}` |
+| `403 Forbidden` | `ApiResponse` | `{"success":false,"message":"You do not own this codebase","data":null}` |
+| `404 Not Found` | `ApiResponse` | `{"success":false,"message":"Codebase not found","data":null}` |
+| `409 Conflict` | `ApiResponse` | `{"success":false,"message":"Codebase is currently being indexed","data":null}` |
+
+### POST /api/codebases/{codebaseId}/chat/stream
+
+- Purpose: Stream an AI response grounded in the selected codebase.
+- Authentication/authorization: Authenticated user required; the requester must own the target codebase.
+- Request headers/path/query parameters:
+
+| Name | Type | Required | Constraints | Description |
+|---|---|---:|---|---|
+| `Authorization` | string | Yes | Authentication is enforced by security configuration. | Authenticated principal. |
+| `codebaseId` | UUID | Yes | Valid UUID path value. | Target codebase. |
+
+- Request body: `CodeChatRequest`
+
+| Field | Type | Required | Nullability | Constraints | Description |
+|---|---|---:|---|---|---|
+| `chatId` | string | No | Nullable | Blank or null becomes `default`. | Conversation identifier. |
+| `message` | string | Yes | Non-null | `@NotBlank(message = "message is required")`; trimmed before use. | User prompt. |
+
+Example:
+
+```json
+{
+ "chatId": "architecture-1",
+ "message": "How does clone processing work?"
+}
+```
+
+- Responses:
+
+| Status | Body | Description |
+|---|---|---|
+| `200 OK` | `text/event-stream` | Emits `message` events for response text, then `citations`, then `done`. |
+| `400 Bad Request` | Validation error envelope | Request validation failed before streaming. |
+| `403 Forbidden` | `ApiResponse` | Authenticated user does not own the codebase. |
+| `404 Not Found` | `ApiResponse` | Codebase does not exist. |
+
+- Stream events:
+ - `message`: JSON string containing an assistant text fragment.
+ - `citations`: JSON array of `CodeCitation` values.
+ - `done`: JSON string containing the normalized chat ID.
+ - `error`: JSON object `{"message":"Unable to complete chat"}` when streaming fails.
+
+## WebSocket/message contracts
+
+No WebSocket or STOMP handlers are defined. Realtime delivery is provided through the SSE endpoint documented above.
+
+## Shared schemas
+
+### ApiResponse
+
+| Field | Type | Required | Nullability | Meaning |
+|---|---|---:|---|---|
+| `success` | boolean | Yes | Non-null | Whether the operation succeeded. |
+| `message` | string | Yes | Non-null | Human-readable result or error message. |
+| `data` | T | No | Nullable/empty | Operation payload. The Java source models this with `Optional`. |
+
+### CodebaseResponse
+
+| Field | Type | Required | Nullability | Meaning |
+|---|---|---:|---|---|
+| `codebaseId` | UUID | Yes | Non-null | Codebase identity. |
+| `name` | string | Yes | Non-null | Display name. |
+| `cloneUrl` | string | No | Nullable | Repository clone URL. |
+| `branch` | string | No | Nullable | Indexed branch. |
+| `status` | CodebaseStatus | Yes | Non-null | Current indexing state. |
+| `lastCommitSha` | string | No | Nullable | Last resolved repository commit. |
+| `indexedAt` | timestamp | No | Nullable | Time indexing reached `INDEXED`. |
+| `createdAt` | timestamp | Yes | Non-null | Audit creation time. |
+| `updatedAt` | timestamp | Yes | Non-null | Audit update time. |
+| `fileCount` | integer | Yes | Non-null | Number of repository file rows associated with the codebase. |
+
+### CodebaseImportResponse
+
+| Field | Type | Required | Nullability | Meaning |
+|---|---|---:|---|---|
+| `codebaseId` | UUID | Yes | Non-null | Codebase identity. |
+| `status` | CodebaseStatus | Yes | Non-null | Current state; queued responses use `QUEUED`. |
+| `fileCount` | integer | Yes | Non-null | Processed file count; queued responses use `0`. |
+
+### CodebaseStatus
+
+Enum values: `INDEXED`, `QUEUED`, `PROCESSING`, `FAILED`.
+
+### CodeCitation
+
+| Field | Type | Required | Nullability | Meaning |
+|---|---|---:|---|---|
+| `chunkId` | UUID | Yes | Non-null | Referenced code chunk. |
+| `path` | string | Yes | Non-null | Repository-relative path. |
+| `startLine` | integer | Yes | Nullable | Citation start line when available. |
+| `endLine` | integer | Yes | Nullable | Citation end line when available. |
+| `language` | string | Yes | Nullable | Detected language when available. |
+| `distance` | number | Yes | Non-null | Similarity distance. |
+
+### Domain and validation errors
+
+`CodebaseException` errors use the exception’s configured HTTP status and are returned as `ApiResponse` with
+`success=false`, the exception message, and empty data. Validation failures use `ApiResponse