Skip to content

Commit 705b1d2

Browse files
committed
NLP implemengted to create certian features
1 parent 3044d40 commit 705b1d2

13 files changed

Lines changed: 741 additions & 27 deletions

File tree

‎CLAUDE.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ All providers talk directly to their APIs over native `fetch` (no vendor SDKs).
121121
| `azure` | `gpt-4o` | `api-key` header — set `AI_BASE_URL` to the full deployment endpoint incl. `?api-version=…` |
122122
| `ollama` | `llama3` | `Authorization: Bearer ollama`; default base `http://localhost:11434` |
123123

124-
`lib/sseLines.ts` is the shared SSE line reader used by every streaming provider. Currently only `POST /api/ai/cards/:id/summarize` consumes the provider.
124+
`lib/sseLines.ts` is the shared SSE line reader used by every streaming provider. Two AI endpoints are currently implemented:
125+
- `POST /api/ai/cards/:id/summarize` — generates a 2–3 sentence summary of a story card
126+
- `POST /api/ai/parse-item` — parses natural-language work item requests and returns a discriminated union with type + payload (used for universal NL input across the board, epics, sprints, calendar, and project creation)
125127

126128
## Real-time (SSE)
127129

‎client/CLAUDE.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Scoped guidance for Claude Code when editing under `client/`. For repo-wide cont
6565
- `CardModal` — full story editor; **5 tabs**: Description (with inline Tasks checklist), Comments, Activity, Tests, Dependencies (blocks / blocked-by). Right sidebar holds Sprint, Feature, Assignee, Priority, Story Points selectors
6666
- `ProtectedRoute` — auth gate; redirects to `/login` when no session
6767
- `FeatureGate` — `<FeatureGate flag="ai">{children}</FeatureGate>`; renders only when the flag resolves true
68+
- `NLItemInput` — universal natural-language work item creation (gated by `FEATURE_AI`). Props: `allowedTypes` (array of types the parser can return), `context` (projectId/epicId/laneId), `lanes` (for story lane picker), `cards` (for task parent picker), `onCreated` (refresh callback). State machine: idle → input → loading → preview → confirming. Editable fields appear based on inferred type (priority/assignee for epics/features/stories, dates for sprints/calendar, parent selectors as needed). Wrapped in `<FeatureGate flag="ai">` at every mount site: BoardPage, EpicsPage, SprintsPage, CalendarPage, DashboardPage
6869
- `ProjectAccessModal` — per-user table of all projects with role dropdowns; saves inline on change
6970
- `CreateUserModal` (inline in AdminPage) — chains `POST /users` then `POST /projects/:id/access` per assigned project
7071
- `SettingsTab` (inline in AdminPage) — feature flag toggles; toggle is disabled when the env var is `false` (env is the ceiling)
@@ -84,10 +85,15 @@ Scoped guidance for Claude Code when editing under `client/`. For repo-wide cont
8485
Two files exist; the codebase is mid-migration:
8586

8687
- [src/api.ts](src/api.ts) — fetch-based, flat function exports. Most existing pages import from `'../api'` which resolves to this file (TS prefers the `.ts` over the `api/` directory).
87-
- [src/api/index.ts](src/api/index.ts) — newer axios namespace (`api.projects.list()`, `api.cards.create()`, …). More complete coverage of new endpoints (roadmap, reports, projectAccess).
88+
- [src/api/index.ts](src/api/index.ts) — newer axios namespace (`api.projects.list()`, `api.cards.create()`, …). More complete coverage of new endpoints (roadmap, reports, projectAccess, ai).
8889

8990
**When adding new endpoints:** add to the axios namespace in `api/index.ts`. Don't extend `api.ts` further. If you touch a page that's still on fetch, leave the migration for a separate change unless trivial — do NOT mix axios and fetch in the same component.
9091

92+
**AI methods and types exported from `api/index.ts`:**
93+
- `api.ai.parseItem(data)` — `(input: string, context?: { projectId?, epicId?, laneId?, allowedTypes? }) => Promise<ParsedIntent>` — parses natural-language input into a work item
94+
- `type ParsedIntent = { type, payload } | { type: "unknown", reason }` — discriminated union returned by the `/api/ai/parse-item` endpoint; `type` is one of `"epic"`, `"feature"`, `"story"`, `"task"`, `"project"`, `"sprint"`, `"calendar"`, or `"unknown"`; each type carries different fields in `payload` (e.g., epic/feature/story have priority + assignee, sprint has dates + goal, task has assignee only)
95+
- `type NLAllowedType = "epic" | "feature" | "story" | "task" | "project" | "sprint" | "calendar"` — work item types the parser can infer
96+
9197
Vite proxies `/api` → `localhost:3000` in dev (see `vite.config.ts`). Both clients send credentials so the `sf_token` cookie is included.
9298

9399
## Hooks ([src/hooks/](src/hooks/))

‎client/src/api/index.ts‎

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,27 @@ import type {
1717
RetroCategory,
1818
RetroItem,
1919
Retrospective,
20+
Sprint,
2021
Task,
2122
TestCase,
2223
TestCaseSummary,
2324
TestRun,
2425
TestSuite,
2526
} from '../types'
2627

28+
type Priority = 'low' | 'medium' | 'high' | 'critical'
29+
30+
export type ParsedIntent =
31+
| { type: 'epic' | 'feature'; payload: { title: string; description: string; priority: Priority; assignee: string | null } }
32+
| { type: 'story'; payload: { title: string; description: string; priority: Priority; assignee: string | null; estimate: number | null } }
33+
| { type: 'task'; payload: { title: string; description: string; assignee: string | null } }
34+
| { type: 'project'; payload: { name: string; description: string } }
35+
| { type: 'sprint'; payload: { name: string; goal: string; start_date: string; end_date: string } }
36+
| { type: 'calendar'; payload: { title: string; description: string; start_date: string; end_date: string } }
37+
| { type: 'unknown'; reason: string }
38+
39+
export type NLAllowedType = 'epic' | 'feature' | 'story' | 'task' | 'project' | 'sprint' | 'calendar'
40+
2741
export const http = axios.create({ baseURL: '/api', withCredentials: true })
2842

2943
http.interceptors.response.use(
@@ -50,7 +64,16 @@ function unwrap<T>(p: Promise<{ data: { data: T } }>): Promise<T> {
5064
return p.then(r => r.data.data)
5165
}
5266

67+
// Backward-compatible helper for lane card creation
68+
function createLaneCard(
69+
laneId: number,
70+
data: { title: string; priority?: Card['priority']; assignee?: string | null; sprint_id?: number | null },
71+
): Promise<Card> {
72+
return http.post(`/lanes/${laneId}/cards`, data).then(r => r.data.data)
73+
}
74+
5375
export const api = {
76+
createLaneCard,
5477
projects: {
5578
list: () => unwrap<Project[]>(http.get('/projects')),
5679
get: (id: number) => unwrap<Project>(http.get(`/projects/${id}`)),
@@ -80,7 +103,7 @@ export const api = {
80103
get: (cardId: number) => unwrap<Card>(http.get(`/cards/${cardId}`)),
81104
create: (
82105
laneId: number,
83-
data: { title: string; priority?: Card['priority']; assignee?: string | null; feature_id?: number | null },
106+
data: { title: string; priority?: Card['priority']; assignee?: string | null; feature_id?: number | null; sprint_id?: number | null },
84107
) => unwrap<Card>(http.post(`/lanes/${laneId}/cards`, data)),
85108
update: (
86109
cardId: number,
@@ -120,6 +143,15 @@ export const api = {
120143
delete: (id: number) => unwrap<{ id: number }>(http.delete(`/features/${id}`)),
121144
listStories: (featureId: number) => unwrap<Card[]>(http.get(`/features/${featureId}/stories`)),
122145
},
146+
sprints: {
147+
list: (projectId: number) => unwrap<Sprint[]>(http.get(`/projects/${projectId}/sprints`)),
148+
get: (id: number) => unwrap<Sprint>(http.get(`/sprints/${id}`)),
149+
create: (projectId: number, data: { name: string; goal?: string; start_date: string; end_date: string; status?: Sprint['status'] }) =>
150+
unwrap<Sprint>(http.post(`/projects/${projectId}/sprints`, data)),
151+
update: (id: number, data: Partial<Pick<Sprint, 'name' | 'goal' | 'start_date' | 'end_date' | 'status'>>) =>
152+
unwrap<Sprint>(http.patch(`/sprints/${id}`, data)),
153+
delete: (id: number) => unwrap<{ id: number }>(http.delete(`/sprints/${id}`)),
154+
},
123155
comments: {
124156
list: (cardId: number) => unwrap<Comment[]>(http.get(`/cards/${cardId}/comments`)),
125157
create: (cardId: number, data: { author: string; body: string }) =>
@@ -200,4 +232,8 @@ export const api = {
200232
delete: (id: number) => unwrap<{ id: number }>(http.delete(`/admin/holidays/${id}`)),
201233
},
202234
},
235+
ai: {
236+
parseItem: (data: { input: string; context?: { projectId?: number; epicId?: number; laneId?: number; allowedTypes?: NLAllowedType[] } }) =>
237+
unwrap<ParsedIntent>(http.post('/ai/parse-item', data)),
238+
},
203239
}

0 commit comments

Comments
 (0)