kodu 2.2.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -3
- package/bin/kodu.js +40 -0
- package/package.json +12 -67
- package/scripts/install.js +68 -0
- package/scripts/postinstall.js +22 -0
- package/AGENTS.md +0 -214
- package/__tests__/core/fs/fs.service.test.ts +0 -72
- package/__tests__/core/registry/registry.service.test.ts +0 -82
- package/__tests__/shared/cleaner/cleaner.service.test.ts +0 -102
- package/__tests__/shared/git/git.service.test.ts +0 -84
- package/__tests__/shared/runbook/runbook.service.test.ts +0 -104
- package/__tests__/shared/tokenizer/tokenizer.service.test.ts +0 -45
- package/biome.json +0 -50
- package/dist/package.json +0 -96
- package/dist/src/app.module.d.ts +0 -2
- package/dist/src/app.module.js +0 -42
- package/dist/src/app.module.js.map +0 -1
- package/dist/src/commands/clean/clean.command.d.ts +0 -37
- package/dist/src/commands/clean/clean.command.js +0 -240
- package/dist/src/commands/clean/clean.command.js.map +0 -1
- package/dist/src/commands/clean/clean.module.d.ts +0 -2
- package/dist/src/commands/clean/clean.module.js +0 -26
- package/dist/src/commands/clean/clean.module.js.map +0 -1
- package/dist/src/commands/init/init.command.d.ts +0 -10
- package/dist/src/commands/init/init.command.js +0 -96
- package/dist/src/commands/init/init.command.js.map +0 -1
- package/dist/src/commands/init/init.module.d.ts +0 -2
- package/dist/src/commands/init/init.module.js +0 -22
- package/dist/src/commands/init/init.module.js.map +0 -1
- package/dist/src/commands/ops/ops-add.command.d.ts +0 -18
- package/dist/src/commands/ops/ops-add.command.js +0 -102
- package/dist/src/commands/ops/ops-add.command.js.map +0 -1
- package/dist/src/commands/ops/ops-init.command.d.ts +0 -22
- package/dist/src/commands/ops/ops-init.command.js +0 -130
- package/dist/src/commands/ops/ops-init.command.js.map +0 -1
- package/dist/src/commands/ops/ops-list.command.d.ts +0 -12
- package/dist/src/commands/ops/ops-list.command.js +0 -73
- package/dist/src/commands/ops/ops-list.command.js.map +0 -1
- package/dist/src/commands/ops/ops-path.command.d.ts +0 -9
- package/dist/src/commands/ops/ops-path.command.js +0 -52
- package/dist/src/commands/ops/ops-path.command.js.map +0 -1
- package/dist/src/commands/ops/ops-runbook.command.d.ts +0 -12
- package/dist/src/commands/ops/ops-runbook.command.js +0 -81
- package/dist/src/commands/ops/ops-runbook.command.js.map +0 -1
- package/dist/src/commands/ops/ops-status.command.d.ts +0 -11
- package/dist/src/commands/ops/ops-status.command.js +0 -62
- package/dist/src/commands/ops/ops-status.command.js.map +0 -1
- package/dist/src/commands/ops/ops-use.command.d.ts +0 -12
- package/dist/src/commands/ops/ops-use.command.js +0 -76
- package/dist/src/commands/ops/ops-use.command.js.map +0 -1
- package/dist/src/commands/ops/ops.command.d.ts +0 -7
- package/dist/src/commands/ops/ops.command.js +0 -56
- package/dist/src/commands/ops/ops.command.js.map +0 -1
- package/dist/src/commands/ops/ops.helpers.d.ts +0 -2
- package/dist/src/commands/ops/ops.helpers.js +0 -11
- package/dist/src/commands/ops/ops.helpers.js.map +0 -1
- package/dist/src/commands/ops/ops.module.d.ts +0 -2
- package/dist/src/commands/ops/ops.module.js +0 -36
- package/dist/src/commands/ops/ops.module.js.map +0 -1
- package/dist/src/commands/pack/pack.command.d.ts +0 -51
- package/dist/src/commands/pack/pack.command.js +0 -355
- package/dist/src/commands/pack/pack.command.js.map +0 -1
- package/dist/src/commands/pack/pack.module.d.ts +0 -2
- package/dist/src/commands/pack/pack.module.js +0 -27
- package/dist/src/commands/pack/pack.module.js.map +0 -1
- package/dist/src/core/config/config.module.d.ts +0 -2
- package/dist/src/core/config/config.module.js +0 -23
- package/dist/src/core/config/config.module.js.map +0 -1
- package/dist/src/core/config/config.schema.d.ts +0 -19
- package/dist/src/core/config/config.schema.js +0 -56
- package/dist/src/core/config/config.schema.js.map +0 -1
- package/dist/src/core/config/config.service.d.ts +0 -7
- package/dist/src/core/config/config.service.js +0 -49
- package/dist/src/core/config/config.service.js.map +0 -1
- package/dist/src/core/config/prompt.service.d.ts +0 -10
- package/dist/src/core/config/prompt.service.js +0 -80
- package/dist/src/core/config/prompt.service.js.map +0 -1
- package/dist/src/core/file-system/fs.module.d.ts +0 -2
- package/dist/src/core/file-system/fs.module.js +0 -21
- package/dist/src/core/file-system/fs.module.js.map +0 -1
- package/dist/src/core/file-system/fs.service.d.ts +0 -27
- package/dist/src/core/file-system/fs.service.js +0 -203
- package/dist/src/core/file-system/fs.service.js.map +0 -1
- package/dist/src/core/registry/registry.module.d.ts +0 -2
- package/dist/src/core/registry/registry.module.js +0 -22
- package/dist/src/core/registry/registry.module.js.map +0 -1
- package/dist/src/core/registry/registry.schema.d.ts +0 -24
- package/dist/src/core/registry/registry.schema.js +0 -21
- package/dist/src/core/registry/registry.schema.js.map +0 -1
- package/dist/src/core/registry/registry.service.d.ts +0 -16
- package/dist/src/core/registry/registry.service.js +0 -91
- package/dist/src/core/registry/registry.service.js.map +0 -1
- package/dist/src/core/ui/ui.module.d.ts +0 -2
- package/dist/src/core/ui/ui.module.js +0 -22
- package/dist/src/core/ui/ui.module.js.map +0 -1
- package/dist/src/core/ui/ui.service.d.ts +0 -22
- package/dist/src/core/ui/ui.service.js +0 -43
- package/dist/src/core/ui/ui.service.js.map +0 -1
- package/dist/src/main.d.ts +0 -2
- package/dist/src/main.js +0 -16
- package/dist/src/main.js.map +0 -1
- package/dist/src/shared/cleaner/cleaner.service.d.ts +0 -23
- package/dist/src/shared/cleaner/cleaner.service.js +0 -223
- package/dist/src/shared/cleaner/cleaner.service.js.map +0 -1
- package/dist/src/shared/cleaner/cleaner.types.d.ts +0 -21
- package/dist/src/shared/cleaner/cleaner.types.js +0 -3
- package/dist/src/shared/cleaner/cleaner.types.js.map +0 -1
- package/dist/src/shared/constants.d.ts +0 -4
- package/dist/src/shared/constants.js +0 -113
- package/dist/src/shared/constants.js.map +0 -1
- package/dist/src/shared/deps/deps.module.d.ts +0 -2
- package/dist/src/shared/deps/deps.module.js +0 -21
- package/dist/src/shared/deps/deps.module.js.map +0 -1
- package/dist/src/shared/deps/deps.service.d.ts +0 -15
- package/dist/src/shared/deps/deps.service.js +0 -114
- package/dist/src/shared/deps/deps.service.js.map +0 -1
- package/dist/src/shared/git/git.module.d.ts +0 -2
- package/dist/src/shared/git/git.module.js +0 -21
- package/dist/src/shared/git/git.module.js.map +0 -1
- package/dist/src/shared/git/git.service.d.ts +0 -5
- package/dist/src/shared/git/git.service.js +0 -56
- package/dist/src/shared/git/git.service.js.map +0 -1
- package/dist/src/shared/runbook/runbook.module.d.ts +0 -2
- package/dist/src/shared/runbook/runbook.module.js +0 -22
- package/dist/src/shared/runbook/runbook.module.js.map +0 -1
- package/dist/src/shared/runbook/runbook.service.d.ts +0 -20
- package/dist/src/shared/runbook/runbook.service.js +0 -118
- package/dist/src/shared/runbook/runbook.service.js.map +0 -1
- package/dist/src/shared/runbook/runbook.templates.d.ts +0 -6
- package/dist/src/shared/runbook/runbook.templates.js +0 -49
- package/dist/src/shared/runbook/runbook.templates.js.map +0 -1
- package/dist/src/shared/tokenizer/tokenizer.module.d.ts +0 -2
- package/dist/src/shared/tokenizer/tokenizer.module.js +0 -21
- package/dist/src/shared/tokenizer/tokenizer.module.js.map +0 -1
- package/dist/src/shared/tokenizer/tokenizer.service.d.ts +0 -10
- package/dist/src/shared/tokenizer/tokenizer.service.js +0 -36
- package/dist/src/shared/tokenizer/tokenizer.service.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/docs/todo.md +0 -7
- package/knip.json +0 -10
- package/kodu.json +0 -63
- package/kodu.schema.json +0 -100
- package/lefthook.yml +0 -11
- package/nest-cli.json +0 -8
- package/registry.schema.json +0 -39
- package/scripts/generate-json-schema.ts +0 -27
- package/skills/ac/SKILL.md +0 -239
- package/skills/al/SKILL.md +0 -98
- package/skills/audit/SKILL.md +0 -205
- package/skills/audit/audit-baseline-template.yml +0 -188
- package/skills/audit/runtime-detect.md +0 -64
- package/skills/audit/stacks/_generic.md +0 -41
- package/skills/audit/stacks/_registry.md +0 -47
- package/skills/audit/stacks/go.md +0 -66
- package/skills/audit/stacks/java.md +0 -44
- package/skills/audit/stacks/node.md +0 -57
- package/skills/audit/stacks/python.md +0 -45
- package/skills/audit/stacks/rust.md +0 -44
- package/skills/audit-api-contracts/SKILL.md +0 -201
- package/skills/audit-architecture/SKILL.md +0 -200
- package/skills/audit-bugs/SKILL.md +0 -226
- package/skills/audit-concurrency/SKILL.md +0 -197
- package/skills/audit-deployment/SKILL.md +0 -218
- package/skills/audit-docs/SKILL.md +0 -209
- package/skills/audit-errors/SKILL.md +0 -216
- package/skills/audit-logging/SKILL.md +0 -197
- package/skills/audit-matrix/SKILL.md +0 -245
- package/skills/audit-meta/SKILL.md +0 -120
- package/skills/audit-naming/SKILL.md +0 -200
- package/skills/audit-owasp/SKILL.md +0 -223
- package/skills/audit-performance/SKILL.md +0 -199
- package/skills/audit-reinvention/SKILL.md +0 -214
- package/skills/audit-secrets/SKILL.md +0 -198
- package/skills/audit-tests/SKILL.md +0 -210
- package/skills/audit-validation/SKILL.md +0 -206
- package/skills/audit-verify/SKILL.md +0 -139
- package/skills/audit-yagni/SKILL.md +0 -188
- package/skills/doc-gen/SKILL.md +0 -490
- package/skills/doc-gen/scripts/doc_gen.py +0 -911
- package/skills/generate-project-docs/SKILL.md +0 -380
- package/skills/implement-project/SKILL.md +0 -409
- package/skills/liteend-init/SKILL.md +0 -84
- package/skills/litefront-init/SKILL.md +0 -96
- package/skills/litefront-prototype/SKILL.md +0 -484
- package/skills/ops/SKILL.md +0 -94
- package/skills/post-call-task-builder/SKILL.md +0 -419
- package/skills/project-setup-standardizer/SKILL.md +0 -285
- package/skills/skills-best-practices/SKILL.md +0 -415
- package/skills/start/SKILL.md +0 -319
- package/skills/tech-blueprint/SKILL.md +0 -890
- package/skills/tech-blueprint/scripts/blueprint_validator.py +0 -417
- package/src/app.module.ts +0 -29
- package/src/commands/clean/clean.command.ts +0 -235
- package/src/commands/clean/clean.module.ts +0 -13
- package/src/commands/init/init.command.ts +0 -92
- package/src/commands/init/init.module.ts +0 -9
- package/src/commands/ops/ops-add.command.ts +0 -83
- package/src/commands/ops/ops-init.command.ts +0 -125
- package/src/commands/ops/ops-list.command.ts +0 -57
- package/src/commands/ops/ops-path.command.ts +0 -38
- package/src/commands/ops/ops-runbook.command.ts +0 -74
- package/src/commands/ops/ops-status.command.ts +0 -47
- package/src/commands/ops/ops-use.command.ts +0 -76
- package/src/commands/ops/ops.command.ts +0 -42
- package/src/commands/ops/ops.helpers.ts +0 -20
- package/src/commands/ops/ops.module.ts +0 -23
- package/src/commands/pack/pack.command.ts +0 -347
- package/src/commands/pack/pack.module.ts +0 -14
- package/src/core/config/config.module.ts +0 -10
- package/src/core/config/config.schema.ts +0 -58
- package/src/core/config/config.service.ts +0 -43
- package/src/core/config/prompt.service.ts +0 -80
- package/src/core/file-system/fs.module.ts +0 -8
- package/src/core/file-system/fs.service.ts +0 -248
- package/src/core/registry/registry.module.ts +0 -9
- package/src/core/registry/registry.schema.ts +0 -46
- package/src/core/registry/registry.service.ts +0 -128
- package/src/core/ui/ui.module.ts +0 -9
- package/src/core/ui/ui.service.ts +0 -39
- package/src/main.ts +0 -12
- package/src/shared/cleaner/cleaner.service.ts +0 -289
- package/src/shared/cleaner/cleaner.types.ts +0 -23
- package/src/shared/constants.ts +0 -118
- package/src/shared/deps/deps.module.ts +0 -8
- package/src/shared/deps/deps.service.ts +0 -175
- package/src/shared/git/git.module.ts +0 -8
- package/src/shared/git/git.service.ts +0 -47
- package/src/shared/runbook/runbook.module.ts +0 -9
- package/src/shared/runbook/runbook.service.ts +0 -164
- package/src/shared/runbook/runbook.templates.ts +0 -66
- package/src/shared/tokenizer/tokenizer.module.ts +0 -8
- package/src/shared/tokenizer/tokenizer.service.ts +0 -30
- package/tsconfig.build.json +0 -7
- package/tsconfig.json +0 -28
|
@@ -1,484 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: litefront-prototype
|
|
3
|
-
description: >
|
|
4
|
-
Создание интерактивных прототипов на базе LiteFront (Vite, React 19, TanStack Router,
|
|
5
|
-
URQL, FSD, Tailwind v4 + DaisyUI v5). Приоритет: скорость прототипирования
|
|
6
|
-
и визуальное качество через существующую инфраструктуру проекта.
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# LiteFront Prototype Skill
|
|
10
|
-
|
|
11
|
-
> **ПРОТОТИП — всё симулируется.** Нет реального бэкенда, нет реального API, нет реальной авторизации, нет реальной загрузки файлов. Цель — интерактивный UI, который работает офлайн без внешних зависимостей.
|
|
12
|
-
|
|
13
|
-
## Правила прототипа (строго соблюдать)
|
|
14
|
-
|
|
15
|
-
| Что | Правило |
|
|
16
|
-
|-----|---------|
|
|
17
|
-
| **Авторизация** | `VITE_MOCK_AUTH=true` → `MockAuthProvider`. **Не подключать** Logto / любой OIDC-сервер |
|
|
18
|
-
| **GraphQL API** | Только `createMockExchange` — никаких реальных HTTP-запросов к бэкенду |
|
|
19
|
-
| **Загрузка файлов** | Симулировать через `mockUpload` — никакого реального upload-сервера |
|
|
20
|
-
| **Внешние сервисы** | Любые внешние вызовы — только через моки в памяти |
|
|
21
|
-
| **Генерация типов** | `npm run gen` не нужен — типы писать вручную |
|
|
22
|
-
| **Переменные окружения** | `VITE_GRAPHQL_API_URL` и другие API-переменные игнорируются при мокинге |
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Директория и инициализация
|
|
27
|
-
|
|
28
|
-
Все файлы создавать внутри папки **`prototype/`**.
|
|
29
|
-
|
|
30
|
-
Если `prototype/` пуста или не существует — инициализируй через LiteFront:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npx degit uxname/litefront prototype
|
|
34
|
-
cd prototype
|
|
35
|
-
npm install
|
|
36
|
-
cp .env.example .env
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Сразу после инициализации добавить в `.env`:
|
|
40
|
-
```
|
|
41
|
-
VITE_MOCK_AUTH=true
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
**После инициализации (обязательно):**
|
|
45
|
-
1. **Прочитай `prototype/AGENTS.md`** — главный источник конвенций проекта: структура файлов, архитектура, команды, паттерны кода. Без этого нельзя правильно генерировать код.
|
|
46
|
-
2. Изучи `src/` — какие слои FSD уже есть, какие компоненты существуют.
|
|
47
|
-
3. Проверь `package.json` — список доступных скриптов и зависимостей.
|
|
48
|
-
4. Убедись, что в `.env` установлено `VITE_MOCK_AUTH=true`.
|
|
49
|
-
|
|
50
|
-
## Технологический стек
|
|
51
|
-
|
|
52
|
-
| Слой | Технология |
|
|
53
|
-
|------|-----------|
|
|
54
|
-
| **Билд** | Vite 8 + React 19 + TypeScript 6 (strict) |
|
|
55
|
-
| **Роутинг** | TanStack Router v1 (file-based, авто-код-сплиттинг) |
|
|
56
|
-
| **Данные** | URQL + `createMockExchange` (никакого реального GraphQL API) |
|
|
57
|
-
| **Стейт** | Zustand 5 (локальный), URQL с моками (серверный) |
|
|
58
|
-
| **Стили** | Tailwind v4 + DaisyUI v5 (themes: cmyk / dark) |
|
|
59
|
-
| **Аутентификация** | `MockAuthProvider` через `VITE_MOCK_AUTH=true` (без реального OIDC) |
|
|
60
|
-
| **i18n** | ParaglideJS (Inlang), `messages/{locale}.json` |
|
|
61
|
-
| **UI-кит** | DaisyUI, Lucide-React (иконки), Sonner (тосты) |
|
|
62
|
-
| **Архитектура** | Feature-Sliced Design (FSD) |
|
|
63
|
-
|
|
64
|
-
## Архитектура FSD (Feature-Sliced Design)
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
src/
|
|
68
|
-
├── app/ # Инициализация, провайдеры, ErrorBoundary
|
|
69
|
-
├── entities/ # Бизнес-сущности (counter, user, project…)
|
|
70
|
-
├── features/ # Пользовательские сценарии (auth, createProject…)
|
|
71
|
-
├── pages/ # Компоненты страниц
|
|
72
|
-
├── routes/ # Определения роутов TanStack Router
|
|
73
|
-
├── shared/ # Переиспользуемое: api, ui, config, lib
|
|
74
|
-
├── widgets/ # Композиционные блоки (Header, Sidebar…)
|
|
75
|
-
├── graphql/ # .graphql-файлы (queries/, mutations/, fragments/)
|
|
76
|
-
└── generated/ # Авто-генерация (роуты, GraphQL-типы)
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Правила FSD:**
|
|
80
|
-
- Слой может импортировать только нижележащие слои: `pages` → `widgets` → `features` → `entities` → `shared`.
|
|
81
|
-
- Слайс (папка внутри слоя) не импортирует другие слайсы того же слоя.
|
|
82
|
-
- Публичное API слайса — через `index.ts` (barrel export).
|
|
83
|
-
|
|
84
|
-
**Path aliases** (уже настроены в `tsconfig.json`):
|
|
85
|
-
`@shared/*`, `@entities/*`, `@features/*`, `@widgets/*`, `@pages/*`, `@generated/*`, `@public/*`
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## 1. Существующие компоненты (уже есть в проекте)
|
|
90
|
-
|
|
91
|
-
Не создавай их заново — используй готовые:
|
|
92
|
-
|
|
93
|
-
- **`@features/auth`** — `useAuth()`, `AuthGuard`, `MockAuthProvider`
|
|
94
|
-
- **`@entities/counter`** — `useCounterStore`, `Counter`
|
|
95
|
-
- **`@widgets/Header`** — `Header`
|
|
96
|
-
- **`@shared/ui/ErrorFallback`** — готовая страница ошибки с категориями (AUTH, ACCESS, NETWORK, SERVER, UNKNOWN)
|
|
97
|
-
- **`@shared/ui/Toaster`** — `toast` из Sonner + кастомные стили
|
|
98
|
-
- **`@pages/404`** — страница 404
|
|
99
|
-
|
|
100
|
-
## 2. Роутинг (TanStack Router)
|
|
101
|
-
|
|
102
|
-
Файлы роутов — в `src/routes/`. Роуты разделяют логику (`index.tsx`) и ленивый компонент (`index.lazy.tsx`).
|
|
103
|
-
|
|
104
|
-
**Новый роут:**
|
|
105
|
-
```tsx
|
|
106
|
-
// src/routes/projects.tsx
|
|
107
|
-
import { createFileRoute } from '@tanstack/react-router';
|
|
108
|
-
|
|
109
|
-
export const Route = createFileRoute('/projects')({
|
|
110
|
-
head: () => ({
|
|
111
|
-
meta: [
|
|
112
|
-
{ title: 'Projects | LiteFront' },
|
|
113
|
-
{ name: 'description', content: 'Manage projects' },
|
|
114
|
-
],
|
|
115
|
-
}),
|
|
116
|
-
});
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
```tsx
|
|
120
|
-
// src/routes/projects.lazy.tsx
|
|
121
|
-
import { ProjectsPage } from '@pages/projects';
|
|
122
|
-
import { createLazyFileRoute } from '@tanstack/react-router';
|
|
123
|
-
|
|
124
|
-
export const Route = createLazyFileRoute('/projects')({
|
|
125
|
-
component: ProjectsPage,
|
|
126
|
-
});
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
**Страница:**
|
|
130
|
-
```tsx
|
|
131
|
-
// src/pages/projects/ui/index.tsx
|
|
132
|
-
import { useAuth } from '@features/auth';
|
|
133
|
-
import { Link, useNavigate } from '@tanstack/react-router';
|
|
134
|
-
import { Plus } from 'lucide-react';
|
|
135
|
-
|
|
136
|
-
export function ProjectsPage() {
|
|
137
|
-
const auth = useAuth();
|
|
138
|
-
const navigate = useNavigate();
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<div>
|
|
142
|
-
<div className="flex items-center justify-between mb-6">
|
|
143
|
-
<h1 className="text-2xl font-bold">Projects</h1>
|
|
144
|
-
<button onClick={() => navigate({ to: '/projects/new' })}
|
|
145
|
-
className="btn btn-primary">
|
|
146
|
-
<Plus className="size-4" /> New
|
|
147
|
-
</button>
|
|
148
|
-
</div>
|
|
149
|
-
{auth.isAuthenticated && (
|
|
150
|
-
<p className="text-sm text-base-content/60 mb-4">
|
|
151
|
-
Signed in as {auth.user?.profile?.email}
|
|
152
|
-
</p>
|
|
153
|
-
)}
|
|
154
|
-
<Link to="/projects/$id" params={{ id: '123' }}>Project</Link>
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
**Auth guard на роут (симулированный):**
|
|
161
|
-
```tsx
|
|
162
|
-
// src/routes/protected/index.tsx
|
|
163
|
-
import { createFileRoute, redirect } from '@tanstack/react-router';
|
|
164
|
-
|
|
165
|
-
export const Route = createFileRoute('/protected/')({
|
|
166
|
-
beforeLoad: ({ context }) => {
|
|
167
|
-
if (!context.auth.isAuthenticated) {
|
|
168
|
-
throw redirect({ to: '/' });
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
С `VITE_MOCK_AUTH=true` `context.auth.isAuthenticated` всегда `true` — редирект не произойдёт.
|
|
175
|
-
|
|
176
|
-
**Навигация в коде:** `useNavigate()` из `@tanstack/react-router`.
|
|
177
|
-
**Запрещено** использовать `<a href>`.
|
|
178
|
-
|
|
179
|
-
**После создания нового файла роута — запусти `npm run gen:routes`**
|
|
180
|
-
для перегенерации `src/generated/routeTree.gen.ts`.
|
|
181
|
-
|
|
182
|
-
## 3. Авторизация (только симуляция)
|
|
183
|
-
|
|
184
|
-
`VITE_MOCK_AUTH=true` в `.env` активирует `MockAuthProvider` из `@features/auth`.
|
|
185
|
-
**Не подключать** реальный OIDC-сервер (Logto или любой другой) — прототип работает без него.
|
|
186
|
-
|
|
187
|
-
```tsx
|
|
188
|
-
// Мок-пользователь доступен сразу без логина:
|
|
189
|
-
const auth = useAuth();
|
|
190
|
-
// auth.isAuthenticated === true
|
|
191
|
-
// auth.user?.profile?.email === 'mock@example.com'
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
`AuthGuard` и защищённые роуты работают через мок — дополнительной настройки не нужно.
|
|
195
|
-
|
|
196
|
-
## 4. Данные (только моки, без GraphQL API)
|
|
197
|
-
|
|
198
|
-
В прототипе **нет реального GraphQL API**. Все данные хранятся в памяти и возвращаются через `createMockExchange`.
|
|
199
|
-
Никаких HTTP-запросов к бэкенду. `npm run gen` не нужен.
|
|
200
|
-
|
|
201
|
-
### Mock exchange
|
|
202
|
-
|
|
203
|
-
```ts
|
|
204
|
-
// src/shared/api/mock-exchange.ts
|
|
205
|
-
import { Exchange, makeResult } from 'urql';
|
|
206
|
-
import { pipe, mergeMap, fromValue } from 'wonka';
|
|
207
|
-
|
|
208
|
-
type Mocks = Record<
|
|
209
|
-
string,
|
|
210
|
-
(variables?: Record<string, unknown>) => Record<string, unknown>
|
|
211
|
-
>;
|
|
212
|
-
|
|
213
|
-
export const createMockExchange = (mocks: Mocks): Exchange => {
|
|
214
|
-
return ({ forward }) => (ops$) =>
|
|
215
|
-
pipe(
|
|
216
|
-
ops$,
|
|
217
|
-
mergeMap((operation) => {
|
|
218
|
-
const queryName = operation.query.definitions[0]?.name?.value;
|
|
219
|
-
const handler = queryName ? mocks[queryName] : undefined;
|
|
220
|
-
|
|
221
|
-
if (handler) {
|
|
222
|
-
const data = handler(operation.variables as Record<string, unknown>);
|
|
223
|
-
return fromValue(makeResult(operation, { data }));
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return pipe(fromValue(operation), forward);
|
|
227
|
-
}),
|
|
228
|
-
);
|
|
229
|
-
};
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Подключение в GraphQL-клиент
|
|
233
|
-
|
|
234
|
-
```ts
|
|
235
|
-
// src/shared/api/graphql-client.ts
|
|
236
|
-
import { createMockExchange } from './mock-exchange';
|
|
237
|
-
import type { Mocks } from './mock-exchange';
|
|
238
|
-
|
|
239
|
-
export const createGraphQLClient = (
|
|
240
|
-
accessToken?: string,
|
|
241
|
-
mocks?: Mocks,
|
|
242
|
-
): Client => {
|
|
243
|
-
const exchanges = [
|
|
244
|
-
cacheExchange,
|
|
245
|
-
errorExchange({ onError: (error) => { /* ... */ } }),
|
|
246
|
-
];
|
|
247
|
-
|
|
248
|
-
if (mocks) exchanges.push(createMockExchange(mocks));
|
|
249
|
-
|
|
250
|
-
exchanges.push(fetchExchange);
|
|
251
|
-
|
|
252
|
-
return new Client({
|
|
253
|
-
url: import.meta.env.VITE_GRAPHQL_API_URL,
|
|
254
|
-
exchanges,
|
|
255
|
-
fetchOptions: {
|
|
256
|
-
headers: accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
|
|
257
|
-
},
|
|
258
|
-
requestPolicy: 'cache-and-network',
|
|
259
|
-
});
|
|
260
|
-
};
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
### Мок-данные в `__root.tsx`
|
|
264
|
-
|
|
265
|
-
```tsx
|
|
266
|
-
// src/routes/__root.tsx
|
|
267
|
-
import { GraphQLProvider, createGraphQLClient } from '@shared/api';
|
|
268
|
-
|
|
269
|
-
const mockData = {
|
|
270
|
-
GetProjects: () => ({
|
|
271
|
-
projects: [
|
|
272
|
-
{ id: 'p-1', name: 'Project Alpha', status: 'active' },
|
|
273
|
-
{ id: 'p-2', name: 'Project Beta', status: 'draft' },
|
|
274
|
-
],
|
|
275
|
-
}),
|
|
276
|
-
CreateProject: (vars) => ({
|
|
277
|
-
createProject: { id: 'p-new', name: vars?.input?.name || 'New' },
|
|
278
|
-
}),
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
function RootComponent() {
|
|
282
|
-
const auth = useAuth();
|
|
283
|
-
const client = useMemo(
|
|
284
|
-
() => createGraphQLClient(auth.user?.id_token, mockData),
|
|
285
|
-
[auth.user?.id_token],
|
|
286
|
-
);
|
|
287
|
-
// ...
|
|
288
|
-
}
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### Пример компонента с мок-данными
|
|
292
|
-
|
|
293
|
-
```tsx
|
|
294
|
-
import { useGetProjectsQuery } from '@generated/graphql';
|
|
295
|
-
|
|
296
|
-
function ProjectsList() {
|
|
297
|
-
const [{ data, fetching, error }] = useGetProjectsQuery();
|
|
298
|
-
|
|
299
|
-
if (fetching) return <span className="loading loading-spinner loading-lg" />;
|
|
300
|
-
if (error) return <p className="text-error">{error.message}</p>;
|
|
301
|
-
if (!data?.projects.length)
|
|
302
|
-
return (
|
|
303
|
-
<div className="text-center py-20 bg-base-100 rounded-xl border-2 border-dashed border-base-300">
|
|
304
|
-
<p className="text-base-content/50">No projects yet</p>
|
|
305
|
-
</div>
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
return (
|
|
309
|
-
<div className="overflow-x-auto bg-base-100 rounded-xl shadow-sm border border-base-300">
|
|
310
|
-
<table className="table table-zebra">
|
|
311
|
-
<thead>
|
|
312
|
-
<tr>
|
|
313
|
-
<th>Name</th>
|
|
314
|
-
</tr>
|
|
315
|
-
</thead>
|
|
316
|
-
<tbody>
|
|
317
|
-
{data.projects.map((project) => (
|
|
318
|
-
<tr key={project.id} className="hover">
|
|
319
|
-
<td>{project.name}</td>
|
|
320
|
-
</tr>
|
|
321
|
-
))}
|
|
322
|
-
</tbody>
|
|
323
|
-
</table>
|
|
324
|
-
</div>
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Правила мок-данных
|
|
330
|
-
|
|
331
|
-
1. Все данные — в памяти, никаких внешних сервисов.
|
|
332
|
-
2. UI должен выглядеть как с реальными данными: таблицы непустые, навигация работает, тосты показываются.
|
|
333
|
-
3. Для списков — минимум 2–3 элемента (не пусто, не единичная запись).
|
|
334
|
-
4. Состояния loading/empty/error должны быть визуально видны при соответствующих условиях.
|
|
335
|
-
|
|
336
|
-
### Типы без codegen
|
|
337
|
-
|
|
338
|
-
`npm run gen` не нужен. Типы писать вручную:
|
|
339
|
-
|
|
340
|
-
```ts
|
|
341
|
-
// src/shared/api/mock-types.ts
|
|
342
|
-
export interface Project {
|
|
343
|
-
id: string;
|
|
344
|
-
name: string;
|
|
345
|
-
status: 'active' | 'draft' | 'archived';
|
|
346
|
-
}
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
Либо использовать локальную схему в `codegen.yml`:
|
|
350
|
-
```yaml
|
|
351
|
-
schema: "./src/generated/schema.graphql"
|
|
352
|
-
documents: "./src/graphql/**/*.graphql"
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
## 5. Загрузка файлов (только симуляция)
|
|
356
|
-
|
|
357
|
-
В прототипе файлы **не загружаются на сервер**. Симулировать через `mockUpload`:
|
|
358
|
-
|
|
359
|
-
```ts
|
|
360
|
-
// src/shared/api/mock-upload.ts
|
|
361
|
-
export async function mockUpload(file: File): Promise<{ url: string; name: string }> {
|
|
362
|
-
await new Promise((r) => setTimeout(r, 800)); // имитация задержки
|
|
363
|
-
return {
|
|
364
|
-
url: URL.createObjectURL(file), // временный локальный URL для превью
|
|
365
|
-
name: file.name,
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
```tsx
|
|
371
|
-
import { mockUpload } from '@shared/api/mock-upload';
|
|
372
|
-
import { toast } from '@shared/ui/Toaster';
|
|
373
|
-
|
|
374
|
-
function FileUploader() {
|
|
375
|
-
const [preview, setPreview] = useState<string | null>(null);
|
|
376
|
-
|
|
377
|
-
const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
378
|
-
const file = e.target.files?.[0];
|
|
379
|
-
if (!file) return;
|
|
380
|
-
const result = await mockUpload(file);
|
|
381
|
-
setPreview(result.url);
|
|
382
|
-
toast.success(`Файл загружен: ${result.name}`);
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
return (
|
|
386
|
-
<div>
|
|
387
|
-
<input type="file" onChange={handleFile} className="file-input file-input-bordered" />
|
|
388
|
-
{preview && <img src={preview} alt="preview" className="mt-2 max-h-48 rounded-lg" />}
|
|
389
|
-
</div>
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
Для файлов не-изображений — показывать имя и размер, возвращать мок-URL `/mock/uploaded/<filename>`.
|
|
395
|
-
|
|
396
|
-
## 6. Локальный стейт (Zustand)
|
|
397
|
-
|
|
398
|
-
Для стейта, который не идёт через GraphQL:
|
|
399
|
-
|
|
400
|
-
```tsx
|
|
401
|
-
import { create } from 'zustand';
|
|
402
|
-
import { devtools } from 'zustand/middleware';
|
|
403
|
-
|
|
404
|
-
interface UIState {
|
|
405
|
-
sidebarOpen: boolean;
|
|
406
|
-
toggleSidebar: () => void;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export const useUIStore = create(
|
|
410
|
-
devtools<UIState>((set) => ({
|
|
411
|
-
sidebarOpen: false,
|
|
412
|
-
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
|
|
413
|
-
})),
|
|
414
|
-
);
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
## 7. Стили (Tailwind + DaisyUI)
|
|
418
|
-
|
|
419
|
-
- **DaisyUI:** `btn`, `card`, `input`, `badge`, `modal`, `table`, `table-zebra`, `loading loading-spinner`, `toggle`, `select`, `alert`, `avatar`, `tooltip`.
|
|
420
|
-
- **Модалки:** используй тег `<dialog>` + `useRef`. Открытие: `.showModal()`.
|
|
421
|
-
Закрытие: `<form method="dialog">`.
|
|
422
|
-
- **Иконки:** `lucide-react`.
|
|
423
|
-
- **Темы:** `cmyk` (светлая, по умолчанию), `dark` (тёмная, prefers-color-scheme).
|
|
424
|
-
DaisyUI-темы подключаются через атрибут `data-theme` на `<html>`.
|
|
425
|
-
- **Цвета DaisyUI:** `bg-base-100`, `text-base-content`, `text-primary`,
|
|
426
|
-
`border-base-300`, `bg-primary`, `text-primary-content` и т.д.
|
|
427
|
-
Не используй хардкодные цвета — только семантические токены темы.
|
|
428
|
-
|
|
429
|
-
## 8. i18n (ParaglideJS)
|
|
430
|
-
|
|
431
|
-
Сообщения — в `messages/{locale}.json`. Используй готовые или добавляй новые.
|
|
432
|
-
|
|
433
|
-
```json
|
|
434
|
-
{
|
|
435
|
-
"projects_title": "Projects",
|
|
436
|
-
"project_created": "Project created successfully"
|
|
437
|
-
}
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
Импорт в коде:
|
|
441
|
-
```tsx
|
|
442
|
-
import * as m from '@generated/paraglide/messages';
|
|
443
|
-
// m.projects_title() → "Projects"
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
Для прототипа i18n можно временно пропустить — используй прямые строки.
|
|
447
|
-
|
|
448
|
-
## 9. Порядок действий при генерации кода
|
|
449
|
-
|
|
450
|
-
1. **Прочитай `prototype/AGENTS.md`** — конвенции проекта, структура, паттерны.
|
|
451
|
-
2. Изучи структуру `src/` — какие слайсы FSD уже есть.
|
|
452
|
-
3. Определи, в какой слой FSD ложится новая фича.
|
|
453
|
-
4. **Настрой моки данных** — все операции через `createMockExchange` (никаких реальных API-вызовов).
|
|
454
|
-
5. **Авторизация — только через `VITE_MOCK_AUTH=true`**, не подключать Logto.
|
|
455
|
-
6. **Загрузка файлов — только через `mockUpload`**, без реального сервера.
|
|
456
|
-
7. Добавь мок-данные для новых операций в `mockData` в `__root.tsx`.
|
|
457
|
-
8. Используй существующие компоненты где возможно.
|
|
458
|
-
9. Следуй паттернам из существующих страниц.
|
|
459
|
-
|
|
460
|
-
## 10. Команды
|
|
461
|
-
|
|
462
|
-
```bash
|
|
463
|
-
npm run start:dev # Dev-сервер (localhost:3000)
|
|
464
|
-
npm run gen:routes # Генерация routeTree.gen.ts (нужен после нового роута)
|
|
465
|
-
npm run check # Линтинг + typecheck + Knip
|
|
466
|
-
npm run lint:fix # Biome с автофиксом
|
|
467
|
-
npm run build # Production-сборка
|
|
468
|
-
npm run test:dev # Тесты (Vitest, watch)
|
|
469
|
-
npm run test:e2e:dev # Playwright UI mode
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
`npm run gen` (GraphQL codegen) в прототипе **не нужен** — типы пишутся вручную.
|
|
473
|
-
|
|
474
|
-
## 11. Формат ответа
|
|
475
|
-
|
|
476
|
-
Отвечай **только** блоками кода. Перед каждым блоком указывай путь
|
|
477
|
-
(относительно `prototype/`). Выводи файлы целиком.
|
|
478
|
-
|
|
479
|
-
```
|
|
480
|
-
**`src/pages/projects/ui/index.tsx`**
|
|
481
|
-
\`\`\`tsx
|
|
482
|
-
// полный код файла
|
|
483
|
-
\`\`\`
|
|
484
|
-
```
|
package/skills/ops/SKILL.md
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ops
|
|
3
|
-
description: >
|
|
4
|
-
Работа с проектами и стендами (local/dev/stage/prod) из любого места: глобальный
|
|
5
|
-
реестр проектов, активный стенд, деплой по runbook. Запускай при /ops или запросах
|
|
6
|
-
«задеплой X на dev», «переключи стенд», «как деплоить этот проект», «добавь проект»,
|
|
7
|
-
«куда деплоить», «покажи мои проекты».
|
|
8
|
-
metadata:
|
|
9
|
-
level: multi
|
|
10
|
-
output: ~/.config/kodu/registry.json + <project>/.runbook/
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
# Навык ops — стенды и деплой
|
|
14
|
-
|
|
15
|
-
Этот навык помогает работать со **стендами** проектов: local, dev, stage, prod.
|
|
16
|
-
Он рассчитан на джунов: **тебе не нужно руками править никакие JSON-файлы** — просто
|
|
17
|
-
скажи агенту, что хочешь, а агент сам выполнит нужную команду `kodu ops` или поправит
|
|
18
|
-
файл за тебя.
|
|
19
|
-
|
|
20
|
-
## Главные понятия (простыми словами)
|
|
21
|
-
|
|
22
|
-
- **Стенд** — это окружение, куда мы что-то запускаем: `local` (твой компьютер),
|
|
23
|
-
`dev` (тестовый сервер), `stage` (предпрод), `prod` (боевой, осторожно!).
|
|
24
|
-
- **Реестр** — общий список всех проектов на твоей машине. Файл:
|
|
25
|
-
`~/.config/kodu/registry.json`. Создаётся сам. Благодаря ему `kodu` находит проект
|
|
26
|
-
по имени **из любой папки**.
|
|
27
|
-
- **Runbook** — файл `.runbook/runbook.md` внутри проекта, где по шагам расписано,
|
|
28
|
-
как работать с каждым стендом (куда зайти, какой `docker compose` запустить, как
|
|
29
|
-
склонировать репозиторий). Лежит в `.gitignore` и **не коммитится**.
|
|
30
|
-
- **Активный стенд** — текущий стенд, с которым ты работаешь. Хранится в
|
|
31
|
-
`.runbook/config.json`.
|
|
32
|
-
|
|
33
|
-
## Команды `kodu`, на которые опирается навык
|
|
34
|
-
|
|
35
|
-
| Команда | Что делает |
|
|
36
|
-
|---|---|
|
|
37
|
-
| `kodu ops init` | Настроить стенды в текущем проекте (создаёт `.runbook/`, чинит `.gitignore`, добавляет проект в реестр) |
|
|
38
|
-
| `kodu ops list` | Показать все проекты и их активные стенды |
|
|
39
|
-
| `kodu ops add <name> --path <dir>` | Зарегистрировать проект под уникальным именем |
|
|
40
|
-
| `kodu ops status [name]` | Показать активный стенд проекта |
|
|
41
|
-
| `kodu ops use <stand>` | Переключить активный стенд (в текущем проекте) |
|
|
42
|
-
| `kodu ops use <name> <stand>` | Переключить активный стенд указанного проекта (из любого места) |
|
|
43
|
-
| `kodu ops path <name>` | Напечатать путь к репозиторию (удобно: `cd $(kodu ops path my-api)`) |
|
|
44
|
-
| `kodu ops runbook <name> [stand]` | Показать инструкции по стенду |
|
|
45
|
-
|
|
46
|
-
## Создание и изменение конфига по запросу (ключевой раздел)
|
|
47
|
-
|
|
48
|
-
**Никогда не заставляй пользователя редактировать JSON руками.** Когда он просит
|
|
49
|
-
что-то изменить — сам выполни команду или отредактируй файл, потом покажи результат
|
|
50
|
-
человеческим языком.
|
|
51
|
-
|
|
52
|
-
| Что говорит пользователь | Что делает агент |
|
|
53
|
-
|---|---|
|
|
54
|
-
| «добавь проект my-api, он в ~/work/my-api» | `kodu ops add my-api --path ~/work/my-api` |
|
|
55
|
-
| «настрой стенды здесь» / «инициализируй проект» | `kodu ops init` (из папки проекта), помочь заполнить runbook |
|
|
56
|
-
| «переключи меня на dev» | `kodu ops use dev` |
|
|
57
|
-
| «переключи billing на prod» | `kodu ops use billing prod` (спросить подтверждение, т.к. prod) |
|
|
58
|
-
| «добавь стенд stage в этот проект» | `kodu ops use stage` (стенд добавится автоматически) либо поправить `.runbook/config.json` |
|
|
59
|
-
| «поменяй путь к проекту billing на …» | поправить запись в `~/.config/kodu/registry.json` за пользователя |
|
|
60
|
-
| «какие у меня проекты / стенды?» | `kodu ops list` или `kodu ops status` |
|
|
61
|
-
| «где лежит проект X?» | `kodu ops path X` |
|
|
62
|
-
|
|
63
|
-
После любого изменения — кратко скажи: **что поменял и зачем**, и при необходимости
|
|
64
|
-
покажи `kodu ops status`.
|
|
65
|
-
|
|
66
|
-
## Как задеплоить проект на стенд
|
|
67
|
-
|
|
68
|
-
1. Найди проект: `kodu ops path <name>` (получишь путь) или работай в его папке.
|
|
69
|
-
2. Прочитай инструкции стенда: `kodu ops runbook <name> <stand>` (или открой
|
|
70
|
-
`.runbook/runbook.md`).
|
|
71
|
-
3. Выполни шаги из runbook по порядку (git, `docker compose`, ssh и т.д.).
|
|
72
|
-
**Перед каждой опасной командой покажи её пользователю.**
|
|
73
|
-
4. Если в runbook остались плейсхолдеры `<...>` — попроси у пользователя детали и
|
|
74
|
-
помоги заполнить файл.
|
|
75
|
-
|
|
76
|
-
## Bootstrap нового проекта
|
|
77
|
-
|
|
78
|
-
Если в проекте ещё нет `.runbook/`:
|
|
79
|
-
1. Из папки проекта выполни `kodu ops init` (можно `--name <имя>`, если имя папки
|
|
80
|
-
не подходит).
|
|
81
|
-
2. Простыми словами объясни джуну, что появилось: папка `.runbook/` с `config.json`
|
|
82
|
-
(активный стенд) и `runbook.md` (инструкции), и что `.runbook/` уже добавлен в
|
|
83
|
-
`.gitignore`.
|
|
84
|
-
3. Помоги заполнить `runbook.md` под реальную инфраструктуру.
|
|
85
|
-
|
|
86
|
-
## Гард-рейлы (обязательно)
|
|
87
|
-
|
|
88
|
-
- **prod — только с подтверждением.** Перед любым действием на стенде `prod`
|
|
89
|
-
явно спроси: «Точно деплоим на prod?».
|
|
90
|
-
- **`.gitignore` — автоматически.** Папка `.runbook/` всегда должна быть в
|
|
91
|
-
`.gitignore`; `kodu ops init` делает это сам. Никогда не коммить `.runbook/`.
|
|
92
|
-
- **Показывай команды.** Перед `docker compose down`, удалением, миграциями и
|
|
93
|
-
прочими необратимыми шагами — покажи команду пользователю.
|
|
94
|
-
- **Объясняй изменения.** На каждое изменение конфига — одна строка «что и зачем».
|